You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CWG3219 [basic.types.trivial] Overeager no-op when no suitable value for value representation #980
Full name of submitter (unless configured in github; will be published with the issue): Hubert Tong
Reference (section label): [basic.types.trivial]
Link to reflector thread (if any): N/A
Issue description:
The wording says:
in which case the object’s value is replaced with an unspecified member of the corresponding subset that would result in the program having defined behavior, if any.
In other words, the object's value is unchanged/the object does not receive a value when no such value exists.
This runs into problems with the following:
The symbolic value model does not acknowledge any changes to the value from manipulation of the object representation except when an object acquires a value representation.
bit_cast has undefined behaviour when an object in the result does not receive a value.
For the first, consider:
#include<cstring>intmain(void) {
int x = 42, *p = &x, *q = p + 1;
std::memcpy(&p, &q,
sizeof(p)); // giving `p` the value of `q` would _not_ lead to the// program having defined behaviourreturn *p; // Is the value of `p` here still `&x`? Prior to P2434R5, no.// After: Maybe, but no UB if not (could be `&errno`)—seems// unimplementable in general.
}
For the second, consider:
#include<memory>
#include<cstdio>intmain(void) {
int x = 42, *p = &x, *q = p + 1;
p = std::bit_cast<int *>(q); // UB herestd::fprintf(stderr, "Hello!\n"); // observable checkpoint too latereturn *p;
}
Note that the status quo before P2434R5 already made the bit_cast case undefined behaviour when the value representation did not correspond to any value of the object's type.
Suggested resolution:
in which case the object’s value is replaced with an unspecified member of the corresponding subset, if any, that—if possible—would result in the program having defined behavior, if any. [Note: Lvalue-to-rvalue conversion has undefined behavior if the value representation of the object is not valid for the object's type.]
#include<cstring>intmain(void) {
int x = 42, *p = &x, *q = p + 1;
std::memcpy(&p, &q,
sizeof(p)); // giving `p` the value of `q` would _not_ lead to the// program having defined behaviourreturn *p; // Is the value of `p` here still `&x`? Prior to P2434R5, no.// After: Maybe, but no UB if not (could be `&errno`)—seems// unimplementable in general.
}
@safocl that's the point. Since giving past-the-end value would lead to UB, it is not given
Do you think this applies to the program as a whole? So the implementation should track where the pointer is used later?
In my opinion, no. Why do you think this was done in the first place? (There's a very detailed proposal on this topic, where everything is explained — I haven't seen anything like your reasoning there — the reason, as described there, is completely logical and clear.)
In other words, the object's value is unchanged/the object does not receive a value when no such value exists.
AFAIR, I've seen some well-known C++ community members interpreting sentences like «if Condition, then X happens» as «implicit UB» when the Condition is not true. However, do not remember anyone interpreting object replacement as UB if not transparently replaceable, only that name/pointer/reference won't rebind.
Note that the status quo before P2434R5 already made the bit_cast case undefined behaviour when the value representation did not correspond to any value of the object's type.
but here the value of the pointer to int quite accommodates the value pointing past the end of an object
but here the value of the pointer to int quite accommodates the value pointing past the end of an object
The note is explaining one of the reasons why the proposed resolution does not provide some sort of replacement value in the case where the value representation did not correspond to any value of the object's type and, thereby, leaves such a case with UB from the bit_cast operation. That pre-existing UB in bit_cast is not strictly needed if the result is subject to a temporary materialization conversion.
Here the pointers p1_past_the_end and p2 may have the same value representation, but different values.
Your example (assuming that you adjust it to ensure that a has the necessary alignment) is more about https://wg21.link/p0593 than anything else in the discussion.
The note is explaining one of the reasons why the proposed resolution does not provide some sort of replacement value in the case where the value representation did not correspond to any value of the object's type and, thereby, leaves such a case with UB from the bit_cast operation. That pre-existing UB in bit_cast is not strictly needed if the result is subject to a temporary materialization conversion.
but all values of a pointer to int can be reproduced by a pointer to int
changed the title [-][basic.types.trivial] Overeager no-op when no suitable value for value representation[/-][+]CWG3219 [basic.types.trivial] Overeager no-op when no suitable value for value representation[/+]on Aug 17, 2026
Activity
safocl commentedon Aug 17, 2026
this example contains UB -- the pointer
ppoints past the end of an object and is indirected (expr.unary.op#1).languagelawyer commentedon Aug 17, 2026
@safocl that's the point. Since giving past-the-end value would lead to UB, it is not given
safocl commentedon Aug 17, 2026
Do you think this applies to the program as a whole? So the implementation should track where the pointer is used later?
In my opinion, no. Why do you think this was done in the first place? (There's a very detailed proposal on this topic, where everything is explained — I haven't seen anything like your reasoning there — the reason, as described there, is completely logical and clear.)
languagelawyer commentedon Aug 17, 2026
AFAIR, I've seen some well-known C++ community members interpreting sentences like «if Condition, then X happens» as «implicit UB» when the Condition is not true. However, do not remember anyone interpreting object replacement as UB if not transparently replaceable, only that name/pointer/reference won't rebind.
safocl commentedon Aug 17, 2026
It seems that the idea suggested in the initial topic is based solely on an example similar to this:
Here the pointers
p1_past_the_endandp2may have the same value representation, but different values.safocl commentedon Aug 17, 2026
but here the value of the pointer to int quite accommodates the value pointing past the end of an object
hubert-reinterpretcast commentedon Aug 17, 2026
The note is explaining one of the reasons why the proposed resolution does not provide some sort of replacement value in the case where the value representation did not correspond to any value of the object's type and, thereby, leaves such a case with UB from the
bit_castoperation. That pre-existing UB inbit_castis not strictly needed if the result is subject to a temporary materialization conversion.hubert-reinterpretcast commentedon Aug 17, 2026
Your example (assuming that you adjust it to ensure that
ahas the necessary alignment) is more about https://wg21.link/p0593 than anything else in the discussion.safocl commentedon Aug 17, 2026
but all values of a pointer to int can be reproduced by a pointer to int
safocl commentedon Aug 17, 2026
Thanks, fixed.
[-][basic.types.trivial] Overeager no-op when no suitable value for value representation[/-][+]CWG3219 [basic.types.trivial] Overeager no-op when no suitable value for value representation[/+]jensmaurer commentedon Aug 17, 2026
CWG3219
frederick-vs-ja commentedon Aug 18, 2026
The 2nd example seems also addressed in CWG3039.
safocl commentedon Aug 18, 2026
(so everything was brief and seemed elegant, it all started with the introduction of the [basic.types.trivial] section)