---
title: "Atomics and volatile"
document: P2414R11
date: 2026-06-12
audience: "LEWG, EWG. Goal: Propose an atomics and volatile solution to enable zap-susceptible concurrent algorithms."
reply-to:
  - "Paul E. McKenney <paulmckrcu@kernel.org>"
  - "Peter Sewell, Hans Boehm, Hubert Tong, Niall Douglas, Thomas Rodgers, Will Deacon, Michael Wong, David"
  - "Goldblatt, Kostya Serebryany, Anthony Williams, Tom Scogland, JF Bastien, Daniel Krügler, and David Tenty."
  - "Other contributors: Martin Sebor, Florian Weimer, Davis Herring, Rajan Bhakta, Hal Finkel, Lisa Lippincott, Richard"
  - "Smith, JF Bastien, Davis Herring, Chandler Carruth, Evgenii Stepanov, Scott Schurr, Daveed Vandevoorde, Davis"
  - "Herring, Bronek Kozicki, Jens Gustedt, Peter Sewell, Andrew Tomazos, Davis Herring, Martin Uecker, and Jason"
  - "McGuiness."
  - "Maged Michael <maged.michael@gmail.com>"
---

ISO/IEC JTC1/SC22/WG21 P2414R11, 2026-06-12 Pointer lifetime-end zap proposed solutions

##### Atomics and volatile

**Authors:** Paul E. McKenney (paulmckrcu@kernel.org), Maged Michael (maged.michael@gmail.com), Jens Maurer, Peter Sewell, Hans Boehm, Hubert Tong, Niall Douglas, Thomas Rodgers, Will Deacon, Michael Wong, David Goldblatt, Kostya Serebryany, Anthony Williams, Tom Scogland, JF Bastien, Daniel Krügler, and David Tenty. **Other** contributors: Martin Sebor, Florian Weimer, Davis Herring, Rajan Bhakta, Hal Finkel, Lisa Lippincott, Richard Smith, JF Bastien, Davis Herring, Chandler Carruth, Evgenii Stepanov, Scott Schurr, Daveed Vandevoorde, Davis Herring, Bronek Kozicki, Jens Gustedt, Peter Sewell, Andrew Tomazos, Davis Herring, Martin Uecker, and Jason McGuiness. **Audience:** LEWG, EWG. **Goal:** Propose an atomics and volatile solution to enable zap-susceptible concurrent algorithms.

## Abstract

The C++ standard currently specifies that all pointers to an object become invalid at the end of its lifetime [basic.life]. Although this permits additional diagnostics and optimizations which might be of some value, it is not consistent with long-standing usage, especially for a range of concurrent and sequential algorithms that rely on loads, stores, equality comparisons, and even dereferencing of such pointers. Similar issues result from object-lifetime aspects of C *pointer* *provenance.* **We propose (1) that atomic operations be redefined to yield and to store prospective pointers values and (2)** **that operations on** `volatile` **pointers be defined to yield and to store prospective pointer values.**

## Introduction

The C language has been used to implement low-level concurrent algorithms since at least the early 1980s, and C++ has been put to this use since its inception. However, low-level concurrency capabilities did not officially enter either language until 2011. Given decades of independent evolution of C and C++ on the one hand and concurrency on the other, it should be no surprise that some corner cases were missed in the efforts to add concurrency to C11 and C++11. A number of long-standing and heavily used concurrent algorithms, one of which is presented in a later section, involve loading, storing, casting, and comparing pointers to objects which might have reached their lifetime end between the pointer being loaded and when it is stored, reloaded, cast, and compared, due to concurrent removal and freeing of the pointed-to object. In fact, some long-standing algorithms even rely on dereferencing such pointers, but in C++, only in cases where another object of similar type has since been allocated at the same address. This is problematic given that the current standards and working drafts for both C and C++ do not permit reliable loading, storing, casting, or comparison of such pointers. To quote Section 6.2.4p2 (“Storage durations of objects”) of the ISO C standard:

The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime. (See WG14 [N2369](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2369.pdf) and [N2443](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2443.pdf) for more details on the C language’s handling of pointers to lifetime-ended objects and WG21 [P1726R5](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1726r5.pdf) for the corresponding C++ language details.) However, (1) concurrent algorithms that rely on loading, storing, casting, and comparing such pointer values have been used in production in large bodies of code for decades, (2) automatic recognition of these sorts of algorithms is still very much a research topic (even for small bodies of code), and (3) failures due to non-support of the loading, storing, comparison, and (in certain special cases) dereferencing of such pointers can lead to catastrophic and hard-to-debug failures in systems on which we all depend. We therefore need a solution that not only preserves valuable optimizations and debugging tools, but that also works for existing source code. After all, any solution relying on changes to existing software systems would require that we have a way of locating the vulnerable algorithms, and we currently have no such thing. This is not a new issue: the above semantics have been in the C standard since 1989, and the algorithm called out below was put forward some time before 1973. But this issue’s practical consequences will become more severe as compilers do more optimisation, especially link-time optimisation, and especially given the ubiquity of multi-core hardware. This paper proposes straightforward specific solutions, building on P3347R3 and P2434R1.

## Terminology

*** *Bag of bits:* A simple model of a pointer consisting only of its associated address and type, excluding any
additional information that might be gleaned from lifetime-end pointer zap and pointer provenance. A simple
compiler might well model its pointers as bags of bits. For the purposes of this paper, a non-simple compiler can
be induced to treat pointers as bags of bits by marking all pointer accesses and indirections as `volatile`,
albeit with possible performance degradation.

* *Invalid pointer:* A pointer referencing an object whose storage duration has ended. For more detail, please see
[the “What Does the C++ Standard Say?” section of P1726R5, particularly the reference to section 6.7.5.1p4](https://docs.google.com/document/d/1l1d1f6rtZVOTroUuK5WXuxubZ2sYT6XJwkUoYPIcg2A/edit#heading=h.d9ga4z5sru3)
[basic.stc.general] of the standard (“When the end of the duration of a region of storage is reached, the values of
all pointers representing the address of any part of that region of storage become invalid pointer values”). In the
C standard, such a pointer is termed an *indeterminate* pointer.

* *Invalid pointer* use: Any use of an invalid pointer (including reading, writing, comparison, casting, passing to a
non-deallocation function), and indirection through it. [Intended to correspond to [basic.stc.general] p4 *"Any*
*other use of an invalid pointer value has implementation-defined* behavior."]

*** *Lifetime-end pointer zap:* An event causing a pointer to become invalid, or, in WG14 parlance, indeterminate.
Because this is a WG21 document, the term *becomes invalid* is used in preference to “lifetime-end pointer zap”,
however, text that needs to cover both C++ and C will use the term “lifetime-end pointer zap”, “pointer zap”, or
just “zap”.

* *Pointer provenance:* Implementations are permitted to model pointers as more than just a bag of bits.

*** *Prospective pointer value:* A pointer value corresponding to an object whose lifetime might not have started,
including a pointer to an object whose region of storage has not yet been created. A correct algorithm will not
compare or dereference a prospective pointer until after an appropriately typed object’s lifetime starts at the
address indicated by the pointer’s value. Note that comparison of a prospective pointer’s value representation is
permitted, for example, as carried out by the `.compare_exchange` member function. One way to produce a
prospective pointer is to cast a valid pointer to `uintptr_t` and then cast it back to the same pointer type.
Implementations that do not provide `uintptr_t` can support these changes via the as-if rule: They need not
convert pointers to and from integers, but they must discard any provenance not represented in the
representation value as if they supported `uintptr_t`. For more information, please see [P2434R4](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2434r3.html). Note
however that [P3248R3 ("Require [u]intptr_t")](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3248r3.html) was [forwarded to LWG for C++29](https://github.com/cplusplus/papers/issues/1909), so this might become a
non-issue.

*** *Simple compiler:* A compiler that does no optimization. For the purposes of this paper, results similar to those
of a simple compiler can be obtained by treating all pointers as bags of bits.

*** *Zap-susceptible algorithm:* An algorithm that relies on invalid pointer use and/or zombie pointer dereference.

*** *Zombie pointer:* An invalid pointer whose value representation happens to correspond to the same memory
address as a currently valid pointer to an object of compatible type.

* *Zombie pointer* dereference: Indirection through a zombie pointer. [The relevant part of the standard being
[basic.stc.general] p4: *"Indirection through an invalid pointer value and passing an invalid pointer value to a*
*deallocation function have undefined* behavior."]

## What We Are Asking For

In order to support a number of critically important algorithms, this paper proposes a convenience mechanism in which `std::atomic<T*>` and `volatile` have special behavior so that the associated pointer values become prospective, in the former case, as alluded to in the “Consequences for pointer zap” section of P2434R4. Furthermore, this paper also proposes that `volatile` accesses forgive invalidity in order to support passing of virtual addresses to and from I/O devices, which has long been supported in hardware, either by virtue of that hardware lacking any sort of memory-management unit (MMU) or that hardware being equipped with an I/O MMU that maps addresses provided by hardware devices. For example, consider a device whose firmware and driver are both written in C++. Finally, this paper notes that the implementation must prove that a given pointer is invalid before taking action based on invalidity. The following sections provide more detail on this proposal and also of the options considered since [P1726R4](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1726r4.pdf). Those interested in seeing a wider array of historical options are invited to review [P1726R5](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1726r5.pdf) and [P2188R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2188r1.html). Possible polls:

1. Do we want a convenience mechanism in which `std::atomic<T*>` has special behavior so that the associated
pointer values become prospective, as alluded to in the “Consequences for pointer zap” section of P2434R4?
2. Do we want a convenience mechanism in which `volatile` has special behavior so that the associated pointer
values become prospective?

## Detailed Proposal

As noted earlier, this paper proposes: (1) That atomic operations have the side effect of forgiving pointer invalidity, and (2) that `volatile` accesses have the side effect of forgiving pointer invalidity.

### Atomic Operations Forgive Pointer Invalidity

This section describes a convenience mechanism in which `std::atomic<T*>` has special behavior so that the pointer values become prospective, as described in the “Consequences for pointer zap” section of P2434R4. Atomic operations have the side effect of producing prospective pointer values, thus forgiving pointer invalidity. One way to think of this (due to Davis Herring) is that values stored in atomic pointers are treated as if the member of the `atomic<T*>` type holding the pointer value is of integral type, with each access to that pointer value involving an appropriate cast. This means that provenance is re-evaluated whenever a pointer is loaded from an `atomic<T*>` object. It also means that whenever a pointer is stored to an `atomic<T*>` object, the implementation treats that pointer as having been exposed. Note that this means that an implementation could choose to define `bag_of_bits_ptr<T>` (see separate paper) in terms of `atomic<T*>`.

Although this is an attractive approach, it conflicts with a desire to treat all atomic operations as `constexpr`. We therefore rely on the fact that atomic operations on the full pointer value without specifying the mechanism, thus avoiding this conflict. Although concerns were raised at the 2022 Kona meeting about possible optimization limitations from this approach, the fact is that any thread might update a given atomic pointer at any time, making tracking of provenance through atomic pointers of dubious utility at best. Previous discussions have put forward the notion of “flattening” optimizations that combine all threads into a single thread, with the notion that the implementation might perform exact analysis of this single thread. However, such optimizations can generate infinite loops and deadlocks that would not be present in the original multithreaded code. Given the oracular analysis required to make flattening work for locking and polled atomic operations, the additional analysis required to forgive invalidity for atomic pointers should not be at all difficult by comparison. Whenever a reference to a pointer value is used as the old value by a CAS operation (even a successful one that might not be considered to modify the old value), that pointer value becomes a prospective pointer value. As soon as a value is loaded from an atomic pointer, the resulting non-atomic pointer is immediately subject to any future lifetime-end pointer invalidity. However, as noted earlier, implementations are not permitted to allow this invalidity to affect the value representation.

### Volatile Accesses Forgive Pointer Invalidity

This section describes a convenience mechanism in which `volatile` operations have special behavior so that any associated pointer values become prospective. This means that `volatile` operations on pointers have the side effect of producing prospective pointer values, thus forgiving pointer invalidity. One way to think of this is that the operations accessing such objects use `reinterpret_cast<>` operations, as described in the “Consequences for pointer zap” section of P2434R4. We believe this to be de facto status quo given the current semantics required of `volatile` by real-world device drivers. Note that `volatile` accesses must necessarily forgive invalidity in order to support passing of virtual addresses to and from I/O devices. To see this, keep firmly in mind that the OS kernel (written in C or C++) is communicating via memory with device firmware (also written in C or C++). In other words, the value loaded from a `volatile` pointer might have no relation to the value most recently stored to that same pointer, and all loads and stores are by C or C++ code.

### Examples

#### LIFO Push With Nondeterministic Pointer Provenance

```cpp
Some LIFO Push implementations do not provide direct access to the Node’s pointer, for example, to permit debugging 
hooks to be placed on each store to that pointer. A simple (but according to the standard, buggy) atomic LIFO Push 
indirect-pointer-access algorithm is as follows: 
 
template <typename Node> class LIFOList { // Node must support set_next() 
  std::atomic<Node*> top_{nullptr}; 
 public: 
  void push(Node* newnode) { 
    while (true) { 
      Node* oldtop = top_.load(); // step 1 
      newnode->set_next(oldtop); // step 2 
      if (top_.compare_exchange_weak(oldtop, newnode)) return; // step 3 
    } 
  } 
 
  Node* pop_all() { return top_.exchange(nullptr); } 
}; 
 
Again, note the use of the set_next() member function as opposed to direct access to the pointer linking the nodes in 
the stack. This idiom is used in the wild, for example, in cases where instrumenting this member function assists with 
debugging and performance-analysis tasks. 
 
This code is buggy because it is subject to lifetime-end pointer zap: 
```

##### Use Case 1: Invalid Pointer Use

The following sequence of events illustrates an invalid-pointer vulnerability given the current C++ standard:

* `top_` holds pointer to node `X1` at location `A``.`
  `top_` --> `A` (address of `X1`)

* Thread T1 executes steps 1 and 2 of `push(&X2`).
  `X2.next_` --> `A` (address of `X1`)

* Thread T2 executes `pop_all`, deletes `X1`.
  `X1` deleted
  `top_` --> `null`
  `X2.next_` --> `A` (address of `X1`)

* Thread T1 executes step 3 of `push(&X2`) and **uses invalid pointer** `A` in `.compare_exchange_weak`.

##### Use Case 2: Zombie Pointer Dereference

The following sequence of events illustrates a zombie-pointer vulnerability given the current C++ standard:

* `top_` holds pointer to node `X1` at location `A`.

`top_` --> `A` (address of `X1`)

* Thread T1 executes steps 1 and 2 of `push(&X2).`
  `X2.next_` --> `A` (address of `X1`)

* Thread T2 executes `pop_all`, deletes `X1.`
  `X1` deleted
  `top_` --> `null`
  `X2.next_` --> `A` (address of `X1`)

* Thread T2 allocates node `X3` that happens to be at location `A`, and executes `push(&X3)`
  `top_` --> `A` (address of `X3`)
  `X2.next_` --> `A` (address of `X1` and `X3`) <<<<< **zombie pointer!!!**

* Thread T1 executes step 3 of `push(&X2)` and `.compare_exchange_weak` succeeds
  `top_` --> `&X2`
  `X2.next_` --> `A` (address of `X1` and `X3`)

* Thread T1 executes `pop_all`, dereferences `X2.next_`, which holds value `A` (address of `X1` and `X3`), i.e., a
zombie pointer.

##### Fixing LIFO Push Using This Proposal

```cpp
Assuming non-deterministic pointer provenance (see P2434R4) and defined load, stores, copies, and assignments (see 
P3347R3), the required source-code changes are highlighted in yellow: 
 
template <typename Node> class LIFOList { // Node must support set_next() 
  std::atomic<Node*> top_{nullptr}; 
 public: 
  void push(Node* newnode) { 
    while (true) { 
      Node* oldtop = top_.load(); // step 1 
      newnode->set_next(oldtop); // step 2 
      if (top_.compare_exchange_weak(oldtop, newnode)) return; // step 3 
    } 
  } 
 
  Node* pop_all() { return top_.exchange(nullptr); } 
}; 
 
Alert readers will notice that there is no yellow code, that is, there are no code changes required. 
 
The first use case is fixed in part by the convenience behaviors of std::atomic<T*>, which cause pointers loaded 
from atomics to become prospective. Also required are the additional changes in P3347R3 (“Invalid/Prospective 
Pointer Operations”), which proposes that pointer invalidity not modify value representation and also due to the advent 
of prospective pointer values: 
 
```

* `top_` holds pointer to node `X1` at location `A``.`
  `top_` --> `A` (address of `X1`)

* Thread T1 executes steps 1 and 2 of `push(&X2`).
  `X2.next_` --> `A` (address of `X1`)

* Thread T2 executes `pop_all`, deletes `X1`.
  `X1` deleted
  `top_` --> `null`
  `X2.next_` --> `A` (address of `X1`)

* Thread T1 executes step 3 of `push(&X2`) and uses prospective pointer `A` in `.compare_exchange_weak`.
However, this atomic operation looks only at value representation, which must not be affected by the fact that
the pointer value is prospective, which fixes this example. If the `.compare_exchange_weak` operation fails, this
prospective pointer will remain unused, so no harm is done. Execution will proceed with the new value provided
by that failing `.compare_exchange_weak` operation.

The second use case is fixed in the same way:

* `top_` holds pointer to node `X1` at location `A`.
  `top_` --> `A` (address of `X1`)

* Thread T1 executes steps 1 and 2 of `push(&X2).`
  `X2.next_` --> `A` (address of `X1`, which is a prospective pointer value due to the atomic load.

* Thread T2 executes `pop_all`, deletes `X1.`
  `X1` deleted
  `top_` --> `null`
  `X2.next_` --> `A` (address of `X1`)

* Thread T2 allocates node `X3` that happens to be at location `A`, and executes `push(&X3)`
  `top_` --> `A` (address of `X3`)
  `X2.next_` --> `A` (address of `X1` and `X3`) <<<<< still a prospective pointer value

* Thread T1 executes step 3 of `push(&X2)` and `.compare_exchange_weak` succeeds
  `top_` --> `&X2`
  `X2.next_` --> `A` (address of `X1` and `X3`)

* Thread T1 executes `pop_all`, dereferences `X2.next_`, which holds value `A` (address of `X1` and `X3`), i.e., a
prospective pointer value. However, the referenced object has now been fully constructed, so that this
prospective pointer value is now a valid pointer to the new object. Note that `X3` has been exposed by virtue of
being pushed onto the stack, and having been stored in the atomic object `top_`. Had `X3` not been exposed, the
implementation would have been under no obligation to use its provenance.

These two examples demonstrate use of the changes proposed in this paper.

## Wording

### Atomic Operations And Prospective Pointers

#### # [atomics.types.pointer]

Add after paragraph 1: There is a partial specialization of the atomic class template for pointers. Specializations of this partial specialization are standard-layout structs. They each have a trivial destructor.

Except during constant evaluation, when an operation on an atomic pointer would otherwise result in a pointer value `p` that is not valid in the context of the evaluation [basic.compound], that result instead is `reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(p))`. Descriptions are provided below only for members that differ from the primary template. ***Is it necessary to explicitly call out successful compare-exchange operations converting pointers referenced*** ***by the*** `expected` ***argument from invalid to prospective? I believe that “result in” above covers this case, but*** ***figured that I should check.***

#### atomic_ref and Prospective Pointers

##### # [atomics.ref.pointer]

Add after paragraph 1: There are specializations of the `atomic_ref` class template for all pointer-to-object types. For each such type `pointer-type`, the specialization `atomic_ref<``pointer-type``>` provides additional atomic operations appropriate to pointer types. Except during constant evaluation, when an operation on an atomic pointer would otherwise result in a pointer value `p` that is not valid in the context of the evaluation [basic.compound], that result instead is `reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(p))`. The program is ill-formed if `is_always_lock_free` is `false` and `is_volatile_v<``pointer-type``>` is true.

#### Volatile Operations And Prospective Pointers

##### # [conv.lval]

Add in p3.5:

```cpp
-
   Otherwise, if the bits in the value representation of the object to which the glvalue refers are not valid for the 
   object’s type, the behavior is undefined. 
   [Example 2 : 
   bool f() { 
          bool b = true; 
          char c = 42; 
          memcpy(&b, &c, 1); 
          return b; // undefined behavior if 42 is not a valid value representation for bool 
   } 
   — end example] 
```

- Otherwise, the object indicated by the glvalue is read (3.1). Let `V` be the value contained in the object. If `T` is an integer type, the The prvalue result is

- the value of type `T` congruent (6.9.2) to `V` if `T` is an integer type, - the result of `reinterpret_cast<T>(reinterpretcast<std::uintptr_t>(V))` if `T` is volatile-qualified and `V` is a pointer value that is not valid in the context of the evaluation (6.7.5.5.3), and - `V` otherwise. [Note 2 : See also 7.2.1. — end note]

##### # [expr.assign]

Modify 7.6.19p2 as follows: In simple assignment (`=`), let `V` be the result of the right operand; the object referred to by the left operand is modified (3.1) by replacing its value with `V` or, if the object is of integer type, with

* the value congruent (6.9.2) to `V` if the object is of integer type,

* the result of `reinterpret_cast<T>(reinterpretcast<std::uintptr_t>(V))` if the left operand is a
volatile-qualified glvalue to an object of pointer type `T` and V is a pointer value that is not valid in the context of
the evaluation (6.7.5.5.3), and

* `V` otherwise.

### History

* Update wording based on Tuesday June 9 2026 CWG review with further much-appreciated wording assistance
from Hubert Tong.

* Update atomic and atomic_ref wording based on Thursday June 11 2026 LWG review to permit constexpr
evaluation of atomics and to add the [basic.compound] cross-reference.

* Update atomic and atomic_ref wording based on Thursday June 11 2026 pre-review by Jens Maurer to remove
the unused name “ptr”.

D2414R10 and P2414R10:

* Add an ancient-history section containing Maged Michael’s additional software archaeology on the LIFO Push
algorithm.

P2414R9:

* Split out `bag_of_bits_ptr<T>` and `launder_bag_of_bits_ptr()` into a separate paper, as additional
work appears to be needed to reach agreement on a name.

P2414R8:

* Following Anthony Williams's P2188R1 ("Zap the Zap: Pointers are sometimes just bags of bits"):
○ Rename `usable_ptr<T>` to `bag_of_bits_ptr<T>`.
○ Rename `make_usable_ptr()` to `launder_bag_of_bits_ptr()`.

D2414R8:

* Move the non-direct-pointer access example to D3347R3.

* Update terms based on Davis Herring feedback.

* Add name-selection guide.

* Note relationships to `constexpr` contexts.

P2414R7:

* Rebase discussion onto “[P2434R4 Nondeterministic pointer provenance](https://isocpp.org/files/papers/P2434R4.htmlaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)”.

* Add a LIFO Push algorithm with exposed pointers to help demonstrate the limits of pointer-zap ergonomics if
there is no angelic provenance.

D2414R7:

* Switch comparisons to spaceship operator (`<=>`) based on feedback from SG1 and from Daveed Vandevoorde
at the 2025 Hagenberg meeting.

* Additional changes based on feedback from SG1 at the 2025 Hagenberg meeting:
○ Make only `T&` dereference operator be `constexpr`.
○ Make `operator==()` be `const` rather than `constexpr`.
○ Remove class `D` from hash specification.
○ Change name from `make_ptr_prospective()` to `make_usable_ptr()`.
○ Implementations lacking `uintptr_t` can still implement this via the as-if rule.

* Make the `nullptr_t` constructor for `usable_ptr<T>` initialize `iptr` to zero in order to make it compatible
with the comparison operators.

P2414R6:

* Apply Frank Birbacher feedback:
○ Add page numbers.
○ Fix unbalanced parentheses and double negative in LIFO Push code sample.
○ Add `operator->()`, `operator=()`, and `get()` to `usable_ptr<T>` synopsis.
○ Add comparison operators to `usable_ptr<T>`.
○ Add `nullptr_t` constructor to `usable_ptr<T>`.
○ Define `make_ptr_prospective()` in terms of `usable_ptr<T>`.

* Apply Mark Hoemmen feedback by removing `constexpr` from `usable_ptr<T>` constructors and adding a
sentence explaining why.

* Apply Bryan St. Amour feedback by supplying a `usable_ptr<T>` specialization for `std::hash`.

P2414R5:

* Update references to P2434 to the latest version.

* Move Martin Uecker from author list to contributor list at his request.

* Apply SG1 feedback:
○ Add more alternative names for `usable_ptr<T>`.
○ Fix declaration of * operator for `usable_ptr<T>`.
○ Fix code for casting to `volatile atomic<T>`.
○ Reviewed P2434R1 for “words of power” for prospective provenance, but found none.

* Apply EWG feedback:
○ Rework atomics wording to avoid the need to otherwise duplicate all atomic operations in
[atomics.types.pointer].
○ Add similar wording to [atomics.ref.pointer].
○ Rework volatile wording (also in response to private communications with Davis Herring).
○ Extract the pointer-handling material to [P3347R0 Invalid/Prospective Pointer Operations](https://docs.google.com/document/d/1_58GWhNx_Q9WvhrmnHkaWBD0KoNnmAq-8KTdIXwa-Bc/edit?usp=sharing).

* Updated from “representation bytes” to “value representation” to track [N4993: C++ Working Draft](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/n4993.pdf).

* Updated the definition of “prospective pointer value” to cover the possibility that multiple instances of an object
might be created and deleted before that pointer’s provenance is established.

P2414R4:

* Updated based on the June 24, 2024 St. Louis SG1 review:
○ Fix numerous typos.
○ Drop discussion of defining load, store, and arithmetic operations on invalid and prospective pointers to
allow them to be in their own paper.
○ Add function as well as class.

* Added draft wording and updated per Daniel Krügler feedback.

* Move the history section to the end of the paper.

D2414R4:

* Updated based on the June 24, 2024 St. Louis EWG review and forwarding of [P2434R1: Nondeterministic](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2434r1.html)
[pointer provenance](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2434r1.html) from Davis Herring and subsequent discussions:

○ The prospective-pointer semantics remove the need for a provenance fence, but add the need for a definition of “prospective pointer”. ○ Leverage prospective pointer values. ○ Adjust example code accordingly. P2414R3:

* Includes feedback from the March 20, 2024 Tokyo SG1 and EWG meetings, and also from post-meeting email
reflector discussions.

* Change from reachability to fence semantic, resulting in provenance_fence().

* Add reference to C++ Working Draft [basic.life].

P2414R2:

* Includes feedback from the September 1, 2021 EWG meeting.

* Includes feedback from the November 2022 Kona meeting and subsequent electronic discussions, especially
those with Davis Herring on pointer provenance.

* Includes updates based on inspection of LIFO Push algorithms in the wild, particularly the fact that a LIFO Push
library might not have direct access to the stack node’s pointer to the next node.

* Drops the options not selected to focus on a specific solution, so that P2414R1 serves as an informational
reference for roads not taken.

* Focuses solely on approaches that allow the implementation to reconsider pointer invalidity only at specific
well-marked points in the source code.

P2414R1 captures email-reflector discussions:

* Adds a summary of the requested changes to the abstract.

* Adds a forward reference to detailed expositions for atomics and volatiles to the “What We Are Asking For”
section.

* Add a function `atomic_usable_ref` and change `usable_ptr::ref` to `usable_ref`. Change A2, A3, and
Appendix A accordingly.

* Rewrite of section B5 for clarity.

P2414R0 extracts and builds upon the solutions sections from [P1726R5](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1726r5.pdf) and [P2188R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2188r1.html). Please see [P1726R5](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1726r5.pdf) for discussion of the relevant portions of the standard, rationales for current pointer-zap semantics, expositions of prominent susceptible algorithms, the relationship between pointer zap and both happens-before and value-representation access, and historical discussions of options to handle pointer zap. The WG14 C-Language counterparts to this paper, [N2369](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2369.pdf) and [N2443](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2443.pdf), have been presented at the 2019 London and Ithaca meetings, respectively.

### The Ancient History of LIFO Push

In 1986, R. K. Treiber presented an assembly language implementation of the LIFO Push algorithm in technical report RJ 5118 entitled “Systems Programming: Coping with Parallelism” while at the IBM Almaden Research Center.

In 1975, an [assembly language implementation of this same algorithm](https://x.com/MagedMMichael/status/1946916675355398596/photo/1) (but with `pop()` instead of `popall()`, but with the same ABA properties) was presented in the IBM System 370 Principles of Operation as a method for managing a concurrent freelist. [US Patent 3,886,525](https://patents.google.com/patent/US3886525) was filed in June 1973, and contains a prior-art reference to the LIFO Push algorithm (again with `pop()` instead of `popall()`) as follows: “Conditional swapping of a single address is sufficient to program a last-in, first-out single-user-at-a-time sequencing mechanism.” (If you were to ask a patent attorney, you would likely be told that this 50-year-old patent has long since expired.) We don’t know who first invented LIFO Push or when they invented it, but it was well known in 1973.

### Appendix: Relationship to [WG14 N2676](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2676.pdf)

WG14’s N2676 “A Provenance-aware Memory Object Model for C” is a draft technical specification that aims to clarify pointer provenance, which is related to lifetime-end pointer zap. This technical specification puts forward a number of potential models of pointer provenance, most notably PNVI-ae-udi. This model allows pointer provenance to be restored to pointers whose provenance has previously been stripped (for example, due to the pointer being passed out of the current translation unit as a function parameter and then being passed back in as a return value), but the restored provenance must correspond to a pointer that has been *exposed,* for example, via a conversion to integer, an output operation, or direct access to that pointer’s value representation. Note that `compare_exchange` operations access a pointer’s value representation, and thus expose that pointer. We recommend that other atomic operations also expose pointers passed to them. We also note that given modern I/O devices that operate on virtual-address pointers (using I/O MMUs), volatile stores of pointers must necessarily be considered to be I/O, and thus must expose the pointers that were stored. In addition, either placing a pointer in an object of type `bag_of_bits_ptr<T>` or accessing a pointer as an object of type `bag_of_bits_ptr<T>` exposes that pointer. Finally, note that the changes recommended by N2676 would make casting of pointers through integers a good basis for the `bag_of_bits_ptr<T>` class template. We therefore see N2676 as complementary to and compatible with pointer lifetime-end zap. We do not see either as depending on the other.}

### Appendix: Relation to [WG21 P2434R4](https://isocpp.org/files/papers/P2434R4.html)

WG21’s “P2434R4: Nondeterministic pointer provenance” proposes refinements to the definition of pointer zap. This current paper does not conflict with that paper, but rather builds on top of that paper in order to provide more ergonomic and less user-error-prone ways for the user to avoid pointer zap.
