Document Number: P3248R1.
Date: 2024-06-16.
Reply to: Gonzalo Brito Gadeschi <gonzalob _at_ nvidia.com>.
Authors: Gonzalo Brito Gadeschi.
Audience: SG1, SG22, EWG, LEWG.

Require [u]intptr_t

Changelog

Motivation

Proposals like P2835 and P3125 use [u]intptr_t as an integer type capable of holding a pointer value in their APIs[1]. However, [u]intptr_t being optional forces sub-optimal design choices such as making APIs optional or introducing workarounds.

The potential absence of [u]intptr_t compromises the portability of high-level software and attempts to address this introduce software engineering overheads and potential portability bugs, as seen in libvlc PR#1519.

This proposal advocates for requiring [u]intptr_t in C++ to ensure that all C++ code can rely on integer types capable of holding a pointer value.

Status quo

C Programming language semantics of [u]intptr_t

The ISO/IEC 9899:2023 Working Draft specifies [u]intptr_t semantics as follows:

7.22.1.4 Integer types capable of holding object pointers

  1. The following type designates a signed integer type, other than a bit-precise integer type, with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer
    intptr_t

    The following type designates an unsigned integer type, other than a bit-precise integer type, with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:
    uintptr_t

    These types are optional.

Other sections of the specification provide additional operations that preserve [u]intptr_t values:

ISO/IEC CD TS 6010 - A provenance-aware memory object model for C (N3005) explores extending these guarantees.

C++'s [expr.reinterpret.cast#5] brings C [u]intptr_t semantics into C++ as follows:

A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value ([basic.compound]); mappings between pointers and integers are otherwise implementation-defined.

C++ [cstdio.syn#1] imports frpintf/fscanf from C.

Requiring [u]intptr_t in the C Programming Language

The C programming language proposal N2889 explored requiring [u]intptr_t. It was rejected for C23 but adopted into ISO/IEC CD TS 6010 - A provenance-aware memory object model for C (N3005) to enable C to gain experience with the proposal. There is consensus that this the right approach, but there is not enough implementation experience.

Impact analysis

Impact on conforming implementations

A survey found ubiquitous support for [u]intptr_t in conforming C++ implementations (*):

We did not find any conforming implementation that is inconsistent in C and C++ with respect to the availability of [u]intptr_t: all implementations found provide these types in the headers of both programming languages.

We did not find any conforming implementation that:

Therefore, we conclude that C++ requiring [u]intptr_t:

for any currently conforming implementation.

(*) many C++ implementations are not conforming in one way or antoher, but here we focus on pointers.

Impact on non-conforming implementations

Full support for [u]intptr_t cannot be expected on platforms that lack full support for pointers. All the non-conforming implementations found, are non-conforming with respect to pointer support. For example, their I/O functions (fprintf/fscanf) or memcpy to unaligned addresses do not uphold pointer round-trips (e.g. via %p) validity requirements.

We evaluate the impact on these implementations in terms of what "partial" support for [u]intptr_t can be provided and at what effort (e.g. at least to document which partial support, if any, is provided).

We found that the following non-conforming platforms would not be impacted by C++ requiring [u]intptr_t:

We found that the following non-conforming platforms may be impacted by C++ requiring [u]intptr_t:

Header file inconsistency between C and C++

On a platform in which the C implementation does not provide this type (only non-conforming implementations found), the <stdint.h> header does not provide this type in the C programming language (e.g. when processed by a C compiler).

Per [support.c.header.other.1], in C++ <stdint.h> has the same content as <cstdint>. In a platform in which [u]intptr_t is not available to C via <stdint.h>, it is required to be available to C++ via both <stdint.h> and <cstdint>:

// foo.c - C compiler
#include <stdint.h> // C header processed by C compiler
intptr_t val;       // ill-formed

// bar.cpp - C++ compiler
#include <stdint.h>  // C header processed by C++ compiler
intptr_t val;        // well-formed

// baz.cpp - C++ compiler
#include <cstdint>  // C++ header processed by C++ compiler
std::intptr_t val;  // well-formed

This disconnect in platforms in which the C implementation does not provide [u]intptr_t may impact developer productivity in those platforms.

Design

Design alternatives:

  1. C++ requires [u]intptr_t.
  2. C++ adds new integer types - different from [u]intptr_t - capable of holding a pointer value.
  3. Do nothing.

This proposal advocates for Option 1, i.e., for C++ to require [u]intptr_t, because:

Usage Guideline

[u]intptr_t is well suited for C++ language or C++ Standard Library APIs that need an integer type capable of holding a pointer value, i.e., an integer type with a lossless conversion from/to pointer.

Some features or APIs may only need an integer type capable of holding a pointer address. C and C++ do not currently provide an integer type suited for this use case, but some implementations do provide it as an extension, in platforms were this distinction is crucial, e.g., CHERI C/C++ implementations provide ptraddr_t in <stddef.h> (the CHERI C/C++ Programming Guide is currently outdated and mentions vaddr_t instead of ptraddr_t).

Wording changes

Modify [cstdint.syn]:

  1. The header <cstdint> supplies integer types having specified widths, and macros that specify limits of integer types.

// all freestanding
namespace std {
  using int8_t         = signed integer type;   // optional
  using int16_t        = signed integer type;   // optional
  using int32_t        = signed integer type;   // optional
  using int64_t        = signed integer type;   // optional
  using intN_t         = see below;             // optional

  using int_fast8_t    = signed integer type;
  using int_fast16_t   = signed integer type;
  using int_fast32_t   = signed integer type;
  using int_fast64_t   = signed integer type;
  using int_fastN_t    = see below;             // optional

  using int_least8_t   = signed integer type;
  using int_least16_t  = signed integer type;
  using int_least32_t  = signed integer type;
  using int_least64_t  = signed integer type;
  using int_leastN_t   = see below;             // optional

  using intmax_t       = signed integer type;
  using intptr_t       = signed integer type;   // optional

  using uint8_t        = unsigned integer type; // optional
  using uint16_t       = unsigned integer type; // optional
  using uint32_t       = unsigned integer type; // optional
  using uint64_t       = unsigned integer type; // optional
  using uintN_t        = see below;             // optional

  using uint_fast8_t   = unsigned integer type;
  using uint_fast16_t  = unsigned integer type;
  using uint_fast32_t  = unsigned integer type;
  using uint_fast64_t  = unsigned integer type;
  using uint_fastN_t   = see below;             // optional

  using uint_least8_t  = unsigned integer type;
  using uint_least16_t = unsigned integer type;
  using uint_least32_t = unsigned integer type;
  using uint_least64_t = unsigned integer type;
  using uint_leastN_t  = see below;             // optional

  using uintmax_t      = unsigned integer type;
  using uintptr_t      = unsigned integer type; // optional
}

#define INTN_MIN         see below
#define INTN_MAX         see below
#define UINTN_MAX        see below

#define INT_FASTN_MIN    see below
#define INT_FASTN_MAX    see below
#define UINT_FASTN_MAX   see below

#define INT_LEASTN_MIN   see below
#define INT_LEASTN_MAX   see below
#define UINT_LEASTN_MAX  see below

#define INTMAX_MIN       see below
#define INTMAX_MAX       see below
#define UINTMAX_MAX      see below

#define INTPTR_MIN       see below              // optional
#define INTPTR_MAX       see below              // optional
#define UINTPTR_MAX      see below              // optional

#define PTRDIFF_MIN      see below
#define PTRDIFF_MAX      see below
#define SIZE_MAX         see below

#define SIG_ATOMIC_MIN   see below
#define SIG_ATOMIC_MAX   see below

#define WCHAR_MIN        see below
#define WCHAR_MAX        see below

#define WINT_MIN         see below
#define WINT_MAX         see below

#define INTN_C(value)    see below
#define UINTN_C(value)   see below
#define INTMAX_C(value)  see below
#define UINTMAX_C(value) see below
  1. The header defines all types and macros the same as the C standard library header <stdint.h> except that the types intptr_t and uintptr_t and the macros INTPTR_MIN, INTPTR_MAX, and UINTPTR_MAX are always defined and are not optional. See also: ISO/IEC 9899:2018, 7.20.

  2. All types that use the placeholder N are optional when N is not 8, 16, 32, or 64. The exact-width types intN_t and uintN_t for N = 8, 16, 32, and 64 are also optional; however, if an implementation defines integer types with the corresponding width and no padding bits, it defines the corresponding typedef-names. Each of the macros listed in this subclause is defined if and only if the implementation defines the corresponding typedef-name.
    [Note 1: The macros INTN_C and UINTN_C correspond to the typedef-names int_leastN_t and uint_leastN_t, respectively. — end note]

Acknowledgements

Jens Gustedt for their help with coordinating with WG14, TS 6010, N2889, and establishing a contact with the IBM AS/400 team. Nikolaos Strimpas and and Alibek Omarov for their help in documenting the impact to Elbrus. Aaron Ballman, Jessica Clarke, Jonathan Wakely, Ville Voutilainen, and many others, for feedback that resulted in substantial improvements to the proposal.


  1. This does not imply that these proposals make correct use of these types; the Usage Guideline section covers that. ↩︎