Date: Sat, 12 Apr 2025 16:56:00 +0300
Hi Jens, Shafik, Herb, and others,
Thank you very much for all this work!
I am in fact also working in this area right now: I have been working together with Joshua Berne on a revision of P3100 for Sofia. As part of this work, I *also* went through the entire C++ working paper and enumerated all instances of UB mentioned there. However, I started from scratch rather than basing my work on Shafik's. So we now have our own list of "all instances of UB called out in the C++ Standard".
I think we should have just one such list, so we should merge them. I just went through Jens' PR, reviewed everything you have there, and cross-correlated every \ubref you added in that PR with my own list of UB.
As I was doing that, I noticed that there is a number of instances of UB that is present in my list but absent from your PR. There are also a few other mistakes in your PR and I further have some suggestions on some of the entries in your list. I will type out all this feedback in this email below so you can act on it. Moreover, if you like, Josh and/or I can prepare a PR against your branch that fixes all of the feedback items I am listing below in this email. Please let me know if you think that's a good way forward – or if not, please let me know what we should do instead. We want to help!
One more thing: we did not look at IFNDR as it's not in scope for what Josh and I are currently working on, so this email is just about UB, not about IFNDR.
With all that being said, here is my feedback.
1. The following instances of UB seem to be entirely missing from your PR, i.e., no \ubdef was added to them (unless I missed something or there is a reason why you did not include those specific items – in this case please let me know!). Here is the list of all instances of UB missing from your PR, in the order in which they appear in the C++ working paper (I did not assign any Shafik-style \ubdef-identifiers to them, I just list in which subclause/paragraph they appear):
[basic.align]/1: Attempting to create an object ([intro.object]) in storage that does not meet the alignment requirements of the object's type is undefined behavior.
[basic.stc.dynamic.allocation]/2: The effect of indirecting through a pointer returned from a request for zero size is undefined.
[basic.stc.dynamic.deallocation]/4: If a deallocation function terminates by throwing an exception, the behavior is undefined.
[basic.compound]/4: If a pointer value P is used in an evaluation E and P is not valid in the context of E, then the behavior is undefined if E is an indirection ([expr.unary.op]) or an invocation of a deallocation function ([basic.stc.dynamic.deallocation]).
[basic.lval]/11.3: If a program invokes a defaulted copy/move constructor or copy/move assignment operator for a union of type U with a glvalue argument that does not denote an object of type cv U within its lifetime, the behavior is undefined.
[expr.type]/1: If a pointer to X would be valid in the context of the evaluation of the expression ([basic.fundamental]), the result designates X; otherwise, the behavior is undefined.
[conv.lval]/3.4: 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.
[conv.fpint]/2: If the value being converted is outside the range of values that can be represented, the behavior is undefined.
[conv.ptr]/3: Otherwise, if B is a virtual base class of D and v does not point to an object whose type is similar ([conv.qual]) to D and that is within its lifetime or within its period of construction or destruction ([class.cdtor]), the behavior is undefined.
[conv.mem]/2: If class D does not contain the original member and is not a base class of the class containing the original member, the behavior is undefined.
[expr.dynamic.cast]/7: If v has type “pointer to cv U” and v does not point to an object whose type is similar ([conv.qual]) to U and that is within its lifetime or within its period of construction or destruction ([class.cdtor]), the behavior is undefined.
[expr.dynamic.cast]/7: If v is a glvalue of type U and v does not refer to an object whose type is similar to U and that is within its lifetime or within its period of construction or destruction, the behavior is undefined.
[expr.static.cast]/11: If the prvalue of type “pointer to cv1 B” points to a B that is actually a base class subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the behavior is undefined.
[expr.unary.op]/1: If the operand points to an object or function, the result denotes that object or function; otherwise, the behavior is undefined except as specified in [expr.typeid].
[dcl.ref]/6: Attempting to bind a reference to a function where the converted initializer is a glvalue whose type is not call-compatible ([expr.call]) with the type of the function's definition results in undefined behavior.
[dcl.ref]/6: Attempting to bind a reference to an object where the converted initializer is a glvalue through which the object is not type-accessible ([basic.lval]) results in undefined behavior.
[dcl.ref]/6: The behavior of an evaluation of a reference ([expr.prim.id], [expr.ref]) that does not happen after ([intro.races]) the initialization of the reference is undefined.
[class.cdtor]/1: For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
2. The following items have a \ubdef in your PR, but I don't think they should have one. I don't think they are instances of UB (here I am using *your* \ubdef identifiers to refer to them) and I suggest to remove \ubdef from them:
{class.union.assignment.not.start.lifetime}: This one doesn't actually say that something is UB. It says: "... if modification of X would have undefined behavior under [basic.life], ..." so it merely refers to an existing instance of UB specified elsewhere, it doesn't introduce a new one.
{class.dtor.not.class.type}: This one normatively says that something is UB, but if you read carefully you'll see that it also doesn't introduce anything new, but just refers to an existing instance of UB – I believe this whole paragraph should be a Note instead (please let me know if you want me to open a new Core issue to fix this): "The invocation of a destructor is subject to the usual rules for member functions ([class.mfct]); that is, if the object is not of the destructor's class type and not of a class derived from the destructor's class type (including when the destructor is invoked via a null pointer value), the program has undefined behavior."
3. In the following places, you seem to have mixed up different instances of UB and assigned the wrong \ubdef:
"If the source value is between two adjacent destination values, the result of the conversion is a result of inexact floating-point conversion choice of either of those values. Otherwise, the behavior is undefined." The \ubdef given for this one is {expr.static.cast.downcast.wrong.derived.type}. But the \ubxref for this \ubdef says: "Down-casting to the wrong derived type is undefined behavior." which has nothing to do with floating point conversion at all. An \ubxref for the actual floating-point UB is missing entirely. I think this is an off-by-one error. Suggestion: first, move the \ubdef {expr.static.cast.downcast.wrong.derived.type} where it belongs (into expr.static.cast]/11), then pick a different \ubdef name for this floating point case, then add the missing \ubxref for it.
4. In the following places, you add a single \ubdef, however the paragraph in question actually lists multiple instances of UB which are related but distinct. In my list they are listed as separate cases. You might want to consider doing the same:
{intro.object.implicit.create}: There are two distinct types of UB called out here (emphasis mine): "If no such set of objects would give the program defined behavior, the behavior of the program is undefined" and then later "If no such pointer value would give the program defined behavior, the behavior of the program is undefined."
{expr.delete.mismatch}: There are two distinct types of UB: one for single-object delete and one for array delete. Yet, you treat them as one item here. This is inconsistent with your own PR: later, in {expr.delete.dynamic.type.differ} and {expr.delete.dynamic.array.dynamic.type.differ} you treat single-object delete and array delete as different items.
{class.cdtor.convert.or.form.pointer}: Again, I suggest to list the "convert" and "form a pointer" cases as distinct instances of UB.
5. In the following places, there isn't something broken per se, but I was surprised by your choice for the \ubdef identifier, may I suggest to pick a better one:
{expr.ref.not.similar}: My understanding is that this one is actually about member access, should this be reflected in the identifier?
{expr.mul.div.by.zero}: This is actually about division and modulo, not about multiplication and division. Should this be {expr.div.mod.by.zero} instead?
{expr.mul.representable.type.result}: Similarly, this is about modulo, not about multiplication. Should this be {expr.mod.representable.type.result} instead?
{expr.add.polymorphic}: Why "polymorphic"? What does this have to do with polymorphism? I think this should be {expr.add.not.similar} instead – then it will also be consistent with your existing {expr.ref.not.similar}.
{expr.ass.overlap}: Since we now renamed [expr.ass] to [expr.assign], I believe this one should be {expr.assign.overlap} instead?
That's it for now. I hope this feedback is useful. Any feedback on my feedback would be greatly appreciated. And again, if you would like a PR (on top of yours) that fixes all of the above, please let me know.
Thanks,
Timur
> On 11 Apr 2025, at 18:10, Jens Maurer via Core <core_at_[hidden]> wrote:
>
>
>
> On 04/04/2025 23.41, Herb Sutter wrote:
>> *CWG and Shafik*, direction please re just finding all the places to annotate: As we gear up to enlist more people to help gather the draft annotations (since there's a lot of UB to tag), would you prefer we do it in Shafik's branch? Perhaps as PRs? Shafik, is your branch up to date or do we need to keep it up to date? Just trying to determine where we want to collaborate together to start expanding the list.
>
> So, with Shafik's consent, I've made a branch / pull request for the Annex here,
> based on Shafik's work:
>
> https://github.com/cplusplus/draft/pull/7826
>
> I've fixed various LaTeX issues that prevented a clean CI build.
>
> Any improvements should be pull requests against the "ub-ifndr" branch and will
> amend the pull request above.
>
>> *CWG*, direction please re then actually reviewing batches of those proposed annotations as they get ready to be proposed for the IS draft: Procedurally, for just the annotations of existing UB/IF-NDR (excluding any future 'what to do about each case' changes which would be EWG papers), how does CWG want to see and review batches of annotation additions -- presented as Core wording P papers? as opposed to, say, PRs (which, as was pointed out in EWG, have the drawback of not being available in all countries)?
>
> Shafik's work seems to be pretty comprehensive. There is still work to do
> as outlined in P3075, in particular:
>
> - A summary of the issue in the style of a note
> (We currently have lots of quotes of the normative rule
> in the Annex.)
>
> - No duplication of normative requirements i.e. verbs “shall”, “may”, “might” and “could” are
> forbidden but “can” and “could” are allowed
>
>
> I'd like to have a round of improvements to the existing Annex pull request
> without CWG review, where we also distill a wiki page with rules of
> engagement. We might also want to change some of the presentation.
>
> When we (Herb, Gasper, editors, who else?) are happy with what we have, we'll
> have a CWG review and then merge to the main body. We'll probably have a
> P paper with a "current" rendition of the Annex (just cut the pages from
> the standard PDF) at that point and get plenary approval on the approach;
> I expect further updates to be at the level of "feature-test macros", which
> also are quasi-editorial and don't need plenary approval on the individual level.
>
> Given the completeness we already have, I'm confident we can claim we're
> complete once that P paper gets approved / merged; any residue are "bugs"
> that we can fix in the ordinary course of business.
>
> Jens
>
> _______________________________________________
> Core mailing list
> Core_at_[hidden]
> Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/core
> Link to this post: http://lists.isocpp.org/core/2025/04/17843.php
Thank you very much for all this work!
I am in fact also working in this area right now: I have been working together with Joshua Berne on a revision of P3100 for Sofia. As part of this work, I *also* went through the entire C++ working paper and enumerated all instances of UB mentioned there. However, I started from scratch rather than basing my work on Shafik's. So we now have our own list of "all instances of UB called out in the C++ Standard".
I think we should have just one such list, so we should merge them. I just went through Jens' PR, reviewed everything you have there, and cross-correlated every \ubref you added in that PR with my own list of UB.
As I was doing that, I noticed that there is a number of instances of UB that is present in my list but absent from your PR. There are also a few other mistakes in your PR and I further have some suggestions on some of the entries in your list. I will type out all this feedback in this email below so you can act on it. Moreover, if you like, Josh and/or I can prepare a PR against your branch that fixes all of the feedback items I am listing below in this email. Please let me know if you think that's a good way forward – or if not, please let me know what we should do instead. We want to help!
One more thing: we did not look at IFNDR as it's not in scope for what Josh and I are currently working on, so this email is just about UB, not about IFNDR.
With all that being said, here is my feedback.
1. The following instances of UB seem to be entirely missing from your PR, i.e., no \ubdef was added to them (unless I missed something or there is a reason why you did not include those specific items – in this case please let me know!). Here is the list of all instances of UB missing from your PR, in the order in which they appear in the C++ working paper (I did not assign any Shafik-style \ubdef-identifiers to them, I just list in which subclause/paragraph they appear):
[basic.align]/1: Attempting to create an object ([intro.object]) in storage that does not meet the alignment requirements of the object's type is undefined behavior.
[basic.stc.dynamic.allocation]/2: The effect of indirecting through a pointer returned from a request for zero size is undefined.
[basic.stc.dynamic.deallocation]/4: If a deallocation function terminates by throwing an exception, the behavior is undefined.
[basic.compound]/4: If a pointer value P is used in an evaluation E and P is not valid in the context of E, then the behavior is undefined if E is an indirection ([expr.unary.op]) or an invocation of a deallocation function ([basic.stc.dynamic.deallocation]).
[basic.lval]/11.3: If a program invokes a defaulted copy/move constructor or copy/move assignment operator for a union of type U with a glvalue argument that does not denote an object of type cv U within its lifetime, the behavior is undefined.
[expr.type]/1: If a pointer to X would be valid in the context of the evaluation of the expression ([basic.fundamental]), the result designates X; otherwise, the behavior is undefined.
[conv.lval]/3.4: 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.
[conv.fpint]/2: If the value being converted is outside the range of values that can be represented, the behavior is undefined.
[conv.ptr]/3: Otherwise, if B is a virtual base class of D and v does not point to an object whose type is similar ([conv.qual]) to D and that is within its lifetime or within its period of construction or destruction ([class.cdtor]), the behavior is undefined.
[conv.mem]/2: If class D does not contain the original member and is not a base class of the class containing the original member, the behavior is undefined.
[expr.dynamic.cast]/7: If v has type “pointer to cv U” and v does not point to an object whose type is similar ([conv.qual]) to U and that is within its lifetime or within its period of construction or destruction ([class.cdtor]), the behavior is undefined.
[expr.dynamic.cast]/7: If v is a glvalue of type U and v does not refer to an object whose type is similar to U and that is within its lifetime or within its period of construction or destruction, the behavior is undefined.
[expr.static.cast]/11: If the prvalue of type “pointer to cv1 B” points to a B that is actually a base class subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the behavior is undefined.
[expr.unary.op]/1: If the operand points to an object or function, the result denotes that object or function; otherwise, the behavior is undefined except as specified in [expr.typeid].
[dcl.ref]/6: Attempting to bind a reference to a function where the converted initializer is a glvalue whose type is not call-compatible ([expr.call]) with the type of the function's definition results in undefined behavior.
[dcl.ref]/6: Attempting to bind a reference to an object where the converted initializer is a glvalue through which the object is not type-accessible ([basic.lval]) results in undefined behavior.
[dcl.ref]/6: The behavior of an evaluation of a reference ([expr.prim.id], [expr.ref]) that does not happen after ([intro.races]) the initialization of the reference is undefined.
[class.cdtor]/1: For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
2. The following items have a \ubdef in your PR, but I don't think they should have one. I don't think they are instances of UB (here I am using *your* \ubdef identifiers to refer to them) and I suggest to remove \ubdef from them:
{class.union.assignment.not.start.lifetime}: This one doesn't actually say that something is UB. It says: "... if modification of X would have undefined behavior under [basic.life], ..." so it merely refers to an existing instance of UB specified elsewhere, it doesn't introduce a new one.
{class.dtor.not.class.type}: This one normatively says that something is UB, but if you read carefully you'll see that it also doesn't introduce anything new, but just refers to an existing instance of UB – I believe this whole paragraph should be a Note instead (please let me know if you want me to open a new Core issue to fix this): "The invocation of a destructor is subject to the usual rules for member functions ([class.mfct]); that is, if the object is not of the destructor's class type and not of a class derived from the destructor's class type (including when the destructor is invoked via a null pointer value), the program has undefined behavior."
3. In the following places, you seem to have mixed up different instances of UB and assigned the wrong \ubdef:
"If the source value is between two adjacent destination values, the result of the conversion is a result of inexact floating-point conversion choice of either of those values. Otherwise, the behavior is undefined." The \ubdef given for this one is {expr.static.cast.downcast.wrong.derived.type}. But the \ubxref for this \ubdef says: "Down-casting to the wrong derived type is undefined behavior." which has nothing to do with floating point conversion at all. An \ubxref for the actual floating-point UB is missing entirely. I think this is an off-by-one error. Suggestion: first, move the \ubdef {expr.static.cast.downcast.wrong.derived.type} where it belongs (into expr.static.cast]/11), then pick a different \ubdef name for this floating point case, then add the missing \ubxref for it.
4. In the following places, you add a single \ubdef, however the paragraph in question actually lists multiple instances of UB which are related but distinct. In my list they are listed as separate cases. You might want to consider doing the same:
{intro.object.implicit.create}: There are two distinct types of UB called out here (emphasis mine): "If no such set of objects would give the program defined behavior, the behavior of the program is undefined" and then later "If no such pointer value would give the program defined behavior, the behavior of the program is undefined."
{expr.delete.mismatch}: There are two distinct types of UB: one for single-object delete and one for array delete. Yet, you treat them as one item here. This is inconsistent with your own PR: later, in {expr.delete.dynamic.type.differ} and {expr.delete.dynamic.array.dynamic.type.differ} you treat single-object delete and array delete as different items.
{class.cdtor.convert.or.form.pointer}: Again, I suggest to list the "convert" and "form a pointer" cases as distinct instances of UB.
5. In the following places, there isn't something broken per se, but I was surprised by your choice for the \ubdef identifier, may I suggest to pick a better one:
{expr.ref.not.similar}: My understanding is that this one is actually about member access, should this be reflected in the identifier?
{expr.mul.div.by.zero}: This is actually about division and modulo, not about multiplication and division. Should this be {expr.div.mod.by.zero} instead?
{expr.mul.representable.type.result}: Similarly, this is about modulo, not about multiplication. Should this be {expr.mod.representable.type.result} instead?
{expr.add.polymorphic}: Why "polymorphic"? What does this have to do with polymorphism? I think this should be {expr.add.not.similar} instead – then it will also be consistent with your existing {expr.ref.not.similar}.
{expr.ass.overlap}: Since we now renamed [expr.ass] to [expr.assign], I believe this one should be {expr.assign.overlap} instead?
That's it for now. I hope this feedback is useful. Any feedback on my feedback would be greatly appreciated. And again, if you would like a PR (on top of yours) that fixes all of the above, please let me know.
Thanks,
Timur
> On 11 Apr 2025, at 18:10, Jens Maurer via Core <core_at_[hidden]> wrote:
>
>
>
> On 04/04/2025 23.41, Herb Sutter wrote:
>> *CWG and Shafik*, direction please re just finding all the places to annotate: As we gear up to enlist more people to help gather the draft annotations (since there's a lot of UB to tag), would you prefer we do it in Shafik's branch? Perhaps as PRs? Shafik, is your branch up to date or do we need to keep it up to date? Just trying to determine where we want to collaborate together to start expanding the list.
>
> So, with Shafik's consent, I've made a branch / pull request for the Annex here,
> based on Shafik's work:
>
> https://github.com/cplusplus/draft/pull/7826
>
> I've fixed various LaTeX issues that prevented a clean CI build.
>
> Any improvements should be pull requests against the "ub-ifndr" branch and will
> amend the pull request above.
>
>> *CWG*, direction please re then actually reviewing batches of those proposed annotations as they get ready to be proposed for the IS draft: Procedurally, for just the annotations of existing UB/IF-NDR (excluding any future 'what to do about each case' changes which would be EWG papers), how does CWG want to see and review batches of annotation additions -- presented as Core wording P papers? as opposed to, say, PRs (which, as was pointed out in EWG, have the drawback of not being available in all countries)?
>
> Shafik's work seems to be pretty comprehensive. There is still work to do
> as outlined in P3075, in particular:
>
> - A summary of the issue in the style of a note
> (We currently have lots of quotes of the normative rule
> in the Annex.)
>
> - No duplication of normative requirements i.e. verbs “shall”, “may”, “might” and “could” are
> forbidden but “can” and “could” are allowed
>
>
> I'd like to have a round of improvements to the existing Annex pull request
> without CWG review, where we also distill a wiki page with rules of
> engagement. We might also want to change some of the presentation.
>
> When we (Herb, Gasper, editors, who else?) are happy with what we have, we'll
> have a CWG review and then merge to the main body. We'll probably have a
> P paper with a "current" rendition of the Annex (just cut the pages from
> the standard PDF) at that point and get plenary approval on the approach;
> I expect further updates to be at the level of "feature-test macros", which
> also are quasi-editorial and don't need plenary approval on the individual level.
>
> Given the completeness we already have, I'm confident we can claim we're
> complete once that P paper gets approved / merged; any residue are "bugs"
> that we can fix in the ordinary course of business.
>
> Jens
>
> _______________________________________________
> Core mailing list
> Core_at_[hidden]
> Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/core
> Link to this post: http://lists.isocpp.org/core/2025/04/17843.php
Received on 2025-04-12 13:56:16