std::start_lifetime_as
inadvertently has undefined behavior due to use of std::bit_cast
Section: 20.2.6 [obj.lifetime] Status: New Submitter: Jan Schultke Opened: 2024-10-23 Last modified: 2024-10-31
Priority: Not Prioritized
View all issues with New status.
Discussion:
Consider the motivating example from P2590R2: Explicit lifetime management:
struct X { int a, b; }; X* make_x() { X* p = std::start_lifetime_as<X>(myMalloc(sizeof(struct X)); p->a = 1; p->b = 2; return p; }
Assuming that myMalloc
does not initialize the bytes of storage, this example has undefined behavior because
the value of the resulting object of trivially copyable type X
is determined as if by calling
std::bit_cast<X>(a)
for the implicitly-created object a
of type X
(20.2.6 [obj.lifetime] paragraph 3), whose object representation is filled with indeterminate bytes
obtained from myMalloc
. Such a call to std::bit_cast
has undefined behavior because std::bit_cast
does not tolerate the creation of an int
where bits in the value representation are indeterminate
(22.11.3 [bit.cast] paragraph 2), and such an int
is the smallest enclosing object of some of the
indeterminate bits.
Proposed resolution:
This wording is relative to N4993.
Modify 20.2.6 [obj.lifetime] as indicated:
[Drafting note: The proposed resolution does not alter the behavior for erroneous bits. Therefore, a call to
std::start_lifetime_as
may have erroneous behavior when used on storage with indeterminate bits, despite not accessing that storage. An alternative resolution would be to produce objects whose value is erroneous.]
template<class T> T* start_lifetime_as(void* p) noexcept; template<class T> const T* start_lifetime_as(const void* p) noexcept; template<class T> volatile T* start_lifetime_as(volatile void* p) noexcept; template<class T> const volatile T* start_lifetime_as(const volatile void* p) noexcept;-1- Mandates: […]
-2- Preconditions: […] -3- Effects: Implicitly creates objects (6.7.2 [intro.object]) within the denoted region consisting of an objecta
of typeT
whose address isp
, and objects nested withina
, as follows: The object representation ofa
is the contents of the storage prior to the call tostart_lifetime_as
. The value of each created objecto
of trivially copyable type (6.8.1 [basic.types.general])U
is determined in the same manner as for a call tobit_cast<U>(E)
(22.11.3 [bit.cast]), whereE
is an lvalue of typeU
denotingo
, except that the storage is not accessed and that for each indeterminate bitb
in the value representation of the result, the smallest object containing that bitb
has indeterminate value where the behavior would otherwise be undefined. The value of any other created object is unspecified.