From a09e5e86e901bff6ac9fd02c5d872964a5b6fdc6 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Fri, 2 May 2025 16:32:35 -0400 Subject: [PATCH 1/7] [ub] added many missing entries to UB annex --- source/basic.tex | 8 +- source/classes.tex | 5 +- source/declarations.tex | 6 +- source/expressions.tex | 25 +-- source/front.tex | 2 + source/ub.tex | 335 +++++++++++++++++++++++++++++++++++++++- 6 files changed, 354 insertions(+), 27 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index ffa22b82a2..4cf1138a2b 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -3558,7 +3558,7 @@ using the alignment specifier\iref{dcl.align}. Attempting to create an object\iref{intro.object} in storage that does not meet the alignment requirements of the object's type -is undefined behavior. +is undefined behavior.\ubdef{basic.align.object.alignment} \pnum A \defnadj{fundamental}{alignment} is represented by an alignment @@ -3973,7 +3973,7 @@ \pnum Except in the following cases, if an indeterminate value is produced by an evaluation, -the behavior is undefined, and +the behavior is undefined\ubdef{basic.indet.value}, and if an erroneous value is produced by an evaluation, the behavior is erroneous and the result of the evaluation is the value so produced but is not erroneous: @@ -4300,7 +4300,7 @@ \tcode{p0} represents the address of a block of storage disjoint from the storage for any other object accessible to the caller. The effect of indirecting through a pointer -returned from a request for zero size is undefined. +returned from a request for zero size is undefined.\ubdef{basic.stc.alloc.zero.dereference} \begin{footnote} The intent is to have \tcode{\keyword{operator} \keyword{new}()} implementable by @@ -4423,7 +4423,7 @@ signature. \pnum -If a deallocation function terminates by throwing an exception, the behavior is undefined. +If a deallocation function terminates by throwing an exception, the behavior is undefined.\ubxref{basic.stc.alloc.dealloc.constraint.2} The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect. diff --git a/source/classes.tex b/source/classes.tex index 436d364284..2d2d77867f 100644 --- a/source/classes.tex +++ b/source/classes.tex @@ -6035,9 +6035,10 @@ \indextext{destruction!member access}% For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in -undefined behavior\ubdef{class.cdtor.before.ctor.after.dtor}. For an object with a non-trivial destructor, referring to +undefined behavior\ubdef{class.cdtor.before.ctor.before.ctor}. +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. +execution results in undefined behavior\ubdef{class.cdtor.before.ctor.after.dtor}. \begin{example} \begin{codeblock} struct X { int i; }; diff --git a/source/declarations.tex b/source/declarations.tex index d0faff76a8..b5b60b9020 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -3185,11 +3185,11 @@ the converted initializer is a glvalue whose type is not call-compatible\iref{expr.call} with the type of the function's definition -results in undefined behavior. +results in undefined behavior.\ubdef{dcl.ref.incompatible.function} Attempting to bind a reference to an object where the converted initializer is a glvalue through which the object is not type-accessible\iref{basic.lval} -results in undefined behavior. +results in undefined behavior\ubdef{dcl.ref.incompatible.type}. \begin{note} \indextext{reference!null}% The object designated by such a glvalue can be @@ -3203,7 +3203,7 @@ \end{note} The behavior of an evaluation of a reference\iref{expr.prim.id, expr.ref} that does not happen after\iref{intro.races} the initialization of the reference -is undefined. +is undefined.\ubdef{dcl.ref.uninitialized.reference} \begin{example} \begin{codeblock} int &f(int&); diff --git a/source/expressions.tex b/source/expressions.tex index b24b248021..5c819c1da8 100644 --- a/source/expressions.tex +++ b/source/expressions.tex @@ -322,7 +322,7 @@ a defaulted copy/move constructor or copy/move assignment operator for a union of type \tcode{U} with a glvalue argument that does not denote an object of type \cv{}~\tcode{U} within its lifetime, -the behavior is undefined. +the behavior is undefined.\ubdef{expr.basic.lvalue.union.initialization} \begin{note} In C, an entire object of structure type can be accessed, e.g., using assignment. By contrast, \Cpp{} has no notion of accessing an object of class type @@ -341,7 +341,7 @@ If a pointer to $X$ would be valid in the context of the evaluation of the expression\iref{basic.fundamental}, the result designates $X$; -otherwise, the behavior is undefined. +otherwise, the behavior is undefined.\ubdef{expr.type.reference,lifetime} \begin{note} Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see~\ref{basic.life}). @@ -680,7 +680,7 @@ \item 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. +are not valid for the object's type, the behavior is undefined.\ubdef{conv.lval.valid.representation} \begin{example} \begin{codeblock} bool f() { @@ -997,7 +997,7 @@ integer type. The conversion truncates; that is, the fractional part is discarded. \indextext{value!undefined unrepresentable integral}% -The behavior is undefined\ubdef{conv.fpint.not.represented} if the truncated value cannot be represented +The behavior is undefined\ubdef{conv.fpint.float.not.represented} if the truncated value cannot be represented in the destination type. \begin{note} If the destination type is \keyword{bool}, see~\ref{conv.bool}. @@ -1018,7 +1018,8 @@ exactly as a value of the floating-point type. \end{note} If the value being converted is -outside the range of values that can be represented, the behavior is undefined. If the +outside the range of values that can be represented, the behavior is undefined.\ubdef{conv.fpint.int.not.represented} +If the source type is \keyword{bool}, the value \keyword{false} is converted to zero and the value \keyword{true} is converted to one. @@ -1070,7 +1071,7 @@ that is within its lifetime or within its period of construction or destruction\iref{class.cdtor}, -the behavior is undefined. +the behavior is undefined.\ubdef{conv.ptr.virtual.base} Otherwise, the result is a pointer to the base class subobject of the derived class object. @@ -1104,7 +1105,8 @@ \tcode{D}, a program that necessitates this conversion is ill-formed. If class \tcode{D} does not contain the original member and is not a base class of the class containing the original member, -the behavior is undefined. Otherwise, +the behavior is undefined.\ubdef{conv.member.missing.member} +Otherwise, the result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to @@ -4203,7 +4205,7 @@ that is within its lifetime or within its period of construction or destruction, -the behavior is undefined. +the behavior is undefined.\ubdef{expr.dynamic.cast.lifetime} \pnum If \tcode{T} is ``pointer to \cv{} \keyword{void}'', then the result @@ -4549,7 +4551,7 @@ the result of the conversion is an \impldef{result of inexact floating-point conversion} choice of either of those values. -Otherwise, the behavior is undefined\ubdef{expr.static.cast.downcast.wrong.derived.type}. +Otherwise, the behavior is undefined\ubdef{expr.static.cast.fp.outside.range}. \pnum \indextext{cast!base class}% @@ -4570,7 +4572,7 @@ ``pointer to \cvqual{cv1} \tcode{B}'' points to a \tcode{B} that is actually a base class subobject of an object of type \tcode{D}, the resulting pointer points to the enclosing object of type \tcode{D}. Otherwise, the -behavior is undefined. +behavior is undefined\ubdef{expr.static.cast.downcast.wrong.derived.type}. \pnum \indextext{cast!pointer-to-member}% @@ -4977,7 +4979,8 @@ The operator yields an lvalue of type \tcode{T}. 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 \ref{expr.typeid}. +otherwise, the behavior is undefined except as specified in \ref{expr.typeid} +\ubdef{expr.unary.dereference}. \begin{note} \indextext{type!incomplete}% Indirection through a pointer to an incomplete type (other than diff --git a/source/front.tex b/source/front.tex index 5396b6397d..4689bf813e 100644 --- a/source/front.tex +++ b/source/front.tex @@ -17,6 +17,8 @@ \let\oindex\index \let\index\indexoff +\setcounter{tocdepth}{5} + %% Include table of contents. Do not list "Contents" %% within it (per ISO request) but do include a %% bookmark for it in the PDF. diff --git a/source/ub.tex b/source/ub.tex index 1388ede0d5..135c0e56cc 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -62,6 +62,26 @@ \end{codeblock} \end{example} +\rSec2[ub.basic.align]{Object alignment} + +\pnum +\ubxref{basic.align.object.alignment} \\ +All instances of a type must be created in storage that meets the alignment +requirement of that type. + +\pnum +\begin{example} +\begin{codeblock} +struct alignas(4) S {}; + +void make_misaligned() +{ + alignas(S) char s[sizeof(S) + 1]; + new (&s+1) S(); // undefined behavior +} +\end{codeblock} +\end{example} + \rSec2[ub.basic.life]{Object lifetime} \pnum @@ -219,13 +239,36 @@ \end{codeblock} \end{example} +\rSec2[ub.basic.indet]{Indeterminate and erroneous values} + +\pnum +\ubxref{basic.indet.value} \\ +When the result of an evaluation is +an indeterminate value +(but not just an erroneous value) +the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +void g() { + int x [[ indeterminate ]]; + int y = x; // undefined behavior +} +\end{codeblock} +\end{example} \rSec2[ub.basic.stc.dynamic]{Dynamic storage Duration} \pnum \ubxref{basic.stc.alloc.dealloc.constraint} \\ -If the behavior of an allocation or deallocation function does not satisfy the semantic constraints specified -in \iref{basic.stc.dynamic.allocation} and \iref{basic.stc.dynamic.deallocation}, the behavior is undefined. +% \ubxref{basic.stc.alloc.dealloc.constraint.2} \\ +If the behavior of an allocation or deallocation function does not satisfy the semantic constraints +specified +in \iref{basic.stc.dynamic.allocation} and \iref{basic.stc.dynamic.deallocation}, +including exiting via a thrown exception when not specified to do so, +the behavior is undefined. + \pnum \begin{example} @@ -244,6 +287,25 @@ \end{codeblock} \end{example} +\rSec2[ub.basic.stc.alloc.zero.dereference]{Zero-sized allocation dereference} + +\pnum +\ubxref{basic.stc.alloc.zero.dereference} \\ +The pointer returned when invoking an allocation function with a size of zero +cannot be dereferenced. + +\pnum +\begin{example} +\begin{codeblock} +void test() +{ + char* c = static_cast(operator new(0z)); + c[0] = 'X'; // undefined behavior +} +\end{codeblock} +\end{example} + + \rSec2[ub.intro.execution]{Sequential execution} \pnum @@ -383,8 +445,10 @@ \pnum \ubxref{expr.basic.lvalue.strict.aliasing.violation} \\ -If a program attempts to access \iref{defns.access} the stored value of an object through a glvalue whose type is not -similar \iref{conv.rval} to one of the following types the behavior is undefined +If a program attempts to access \iref{defns.access} the stored value of an object +whose dynamic type is $T$ through a glvalue whose type is not +similar \iref{conv.rval} to $T$ (or its corresponding signed or unsigned types) +the behavior is undefined. \pnum \begin{example} @@ -404,6 +468,67 @@ \end{codeblock} \end{example} +\pnum +\ubxref{expr.basic.lvalue.union.initialization} \\ +If a program invokes a defaulted copy/move constructor or copy/move assignment +operator of a union with an argument that is not an object of a similar type +within its lifetime, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +union U { int x; }; +void f() +{ + char u[sizeof(U)]; + U o = reinterpret_cast(u); // undefined behavior +} +\end{codeblock} +\end{example} + +\rSec2[ub.expr.type]{Type} + +\pnum +\ubxref{expr.type.reference,lifetime} \\ +Evaluating a reference when an equivalent use of a pointer denoting the same object +would be invalid has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +void g() +{ + int* ip = new int(5); + int& i = *ip; + delete ip; + i; // undefined behavior +} +\end{codeblock} +\end{example} + +\rSec2[ub.conv.lval]{Lvalue-to-rvalue conversion} + +\pnum +\ubxref{conv.lval.valid.representation} \\ +Performing an +lvalue-to-rvalue conversion +on an object whose +value representation +is not valid for its type +has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +bool f() { + bool b = true; + char c = 42; + memcpy(&b, &c, 1); + return b; // undefined behavior if \tcode{42} is not a valid value representation for \keyword{bool} +} +\end{codeblock} +\end{example} + \rSec2[ub.conv.double]{Floating-point conversions} @@ -424,7 +549,6 @@ // represetable values are [-inf,+inf] this would not be UB int i = d2; // undefined behavior, the max value of double is not representable as int } - \end{codeblock} \end{example} @@ -432,7 +556,8 @@ \rSec2[ub.conv.fpint]{Floating-integral conversions} \pnum -\ubxref{conv.fpint.not.represented} \\ +\ubxref{conv.fpint.float.not.represented} \\ +%\ubxref{conv.fpint.int.not.represented} \\ % in same section When converting a floating-point value to an integer type and vice versa if the value is not representable in the destination type it is undefined behavior. @@ -455,6 +580,59 @@ \end{codeblock} \end{example} +\rSec2[ub.conv.ptr]{Pointer conversions} + +\pnum +\ubxref{conv.ptr.virtual.base} \\ +Converting +a pointer to a derived class \tcode{D} +to +a pointer to a virtual base class \tcode{B} +that does not point to +a valid object +within its lifetime +has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct B {}; +struct D : virtual B {}; +void f() +{ + D ds[1]; + B* b = &ds[1]; // undefined behavior +} +\end{codeblock} +\end{example} + +\rSec2[ub.conv.mem]{Pointer-to-member conversions} + +\pnum +\ubxref{conv.member.missing.member} \\ +The conversino of +a pointer to a member of a base class +to a pointer to member of a derived class +that coudl not contain that member +has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct B {}; +struct D1 : B { int d1; }; +struct D2 : B {}; +void f() +{ + int (D1::*pd1) = &D1::d1; + int (B::*pb) = static_cast(pd1); + int (D2::*pd2) = pb; // undefined behavior +} +\end{codeblock} +\end{example} + + + \rSec2[ub.expr.call]{Function call} \pnum @@ -499,6 +677,25 @@ \end{codeblock} \end{example} +\rSec2[ub.expr.dynamic.cast]{Dynamic cast} + +\pnum +\ubxref{expr.dynamic.cast.lifetime} \\ +Evaluating a \keyword{dynamic_cast} on a non-null pointer or reference that +denotes an object (of polymorphic type) of the wrong type or that is +not within its lifetime has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct B { virtual ~B(); }; +void f() { + B bs[1]; + B* dp = dynamic_cast(bs+1); // undefined behavior + B& dr = dynamic_cast(bs[1]); // undefined behavior +} +\end{codeblock} +\end{example} \rSec2[ub.expr.static.cast]{Static cast} @@ -539,6 +736,31 @@ \end{codeblock} \end{example} +\pnum +\ubxref{expr.static.cast.fp.outside.range} \\ +An explicit conversion of a +floating-point value that is outside the range of the +target type has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +void f() { + double d = FLT_MAX * 16; + d *= 16; + float f = static_cast(d); // undefined behavior. +} +\end{codeblock} +\end{example} + + +\pnum +\begin{example} +\begin{codeblock} + +\end{codeblock} +\end{example} + \pnum \ubxref{expr.static.cast.downcast.wrong.derived.type} \\ Down-casting to the wrong derived type is undefined behavior. @@ -583,6 +805,24 @@ \end{codeblock} \end{example} +\rSec2[ub.expr.unary.op]{Unary operators} + +\pnum +\ubxref{expr.unary.dereference} \\ +Dereferencing a pointer that does not point to an object or function +has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +int f() +{ + int *p = nullptr; + return *p; // undefined behavior +} +\end{codeblock} +\end{example} + \rSec2[ub.expr.new]{New} @@ -1016,6 +1256,57 @@ \end{codeblock} \end{example} +\rSec2[ub.dcl.ref]{References} + +\pnum +\ubxref{dcl.ref.incompatible.function} \\ +Initializing a reference to a function +with a value that is a function +that is not call-compatible\iref{expr.call} +with the type of the reference +has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +void f(float x); +void (&g)(int) = reinterpret_cast(f); // undefined behavior +\end{codeblock} +\end{example} + +\pnum +\ubxref{dcl.ref.incompatible.type} \\ +Initializing a reference to an object +with a value that is not +type-accessible\iref{basic.lval} through +the type of the reference +has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +float g; +int& i = reinterpret_cast(g); // undefined behavior +\end{codeblock} +\end{example} + +\pnum +\ubxref{dcl.ref.uninitialized.reference} \\ +Evaluating a reference +prior to initializing that +reference has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +extern int &ir1; +int i2 = ir1; // undefined behavior, \tcode{ir1} not yet initialized +int ir1 = 17; +\end{codeblock} +\end{example} + + + \rSec2[ub.dcl.fct.def.coroutine]{Coroutine definitions} @@ -1267,7 +1558,7 @@ \rSec2[ub.class.cdtor]{Construction and destruction} \pnum -\ubxref{class.cdtor.before.ctor.after.dtor} \\ +\ubxref{class.cdtor.before.ctor.before.ctor} \\ For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. 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 @@ -1316,6 +1607,35 @@ \end{example} +\pnum +\ubxref{class.cdtor.before.ctor.after.dtor} \\ +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 +has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct X { + int i; + ~X(); // non-trivial +}; +X& g() +{ + static X x; + return x; +} +void f() +{ + X& px = &g(); + px->~X(); + int*p = px->i; // undefined behavior +} +\end{codeblock} +\end{example} + + \pnum \ubxref{class.cdtor.convert.or.form.pointer} \\ When converting a pointer to a base class of an object or forming a pointer to a direct non-static @@ -1645,3 +1965,4 @@ #define __cplusplus 300012L \end{codeblock} \end{example} + From 1182ecc9577ab43605912b33d9f8d06980504ab7 Mon Sep 17 00:00:00 2001 From: notadragon Date: Sun, 4 May 2025 23:37:24 -0400 Subject: [PATCH 2/7] fixups: addressing check-source issues --- source/ub.tex | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/source/ub.tex b/source/ub.tex index 135c0e56cc..5ee0c26067 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -77,7 +77,7 @@ void make_misaligned() { alignas(S) char s[sizeof(S) + 1]; - new (&s+1) S(); // undefined behavior + new (&s+1) S(); // undefined behavior } \end{codeblock} \end{example} @@ -253,7 +253,7 @@ \begin{codeblock} void g() { int x [[ indeterminate ]]; - int y = x; // undefined behavior + int y = x; // undefined behavior } \end{codeblock} \end{example} @@ -300,7 +300,7 @@ void test() { char* c = static_cast(operator new(0z)); - c[0] = 'X'; // undefined behavior + c[0] = 'X'; // undefined behavior } \end{codeblock} \end{example} @@ -481,7 +481,7 @@ void f() { char u[sizeof(U)]; - U o = reinterpret_cast(u); // undefined behavior + U o = reinterpret_cast(u); // undefined behavior } \end{codeblock} \end{example} @@ -501,7 +501,7 @@ int* ip = new int(5); int& i = *ip; delete ip; - i; // undefined behavior + i; // undefined behavior } \end{codeblock} \end{example} @@ -585,7 +585,7 @@ \pnum \ubxref{conv.ptr.virtual.base} \\ Converting -a pointer to a derived class \tcode{D} +a pointer to a derived class \tcode{D} to a pointer to a virtual base class \tcode{B} that does not point to @@ -601,7 +601,7 @@ void f() { D ds[1]; - B* b = &ds[1]; // undefined behavior + B* b = &ds[1]; // undefined behavior } \end{codeblock} \end{example} @@ -818,7 +818,7 @@ int f() { int *p = nullptr; - return *p; // undefined behavior + return *p; // undefined behavior } \end{codeblock} \end{example} @@ -1270,7 +1270,7 @@ \begin{example} \begin{codeblock} void f(float x); -void (&g)(int) = reinterpret_cast(f); // undefined behavior +void (&g)(int) = reinterpret_cast(f); // undefined behavior \end{codeblock} \end{example} @@ -1286,7 +1286,7 @@ \begin{example} \begin{codeblock} float g; -int& i = reinterpret_cast(g); // undefined behavior +int& i = reinterpret_cast(g); // undefined behavior \end{codeblock} \end{example} @@ -1300,7 +1300,7 @@ \begin{example} \begin{codeblock} extern int &ir1; -int i2 = ir1; // undefined behavior, \tcode{ir1} not yet initialized +int i2 = ir1; // undefined behavior, \tcode{ir1} not yet initialized int ir1 = 17; \end{codeblock} \end{example} @@ -1619,7 +1619,7 @@ \begin{codeblock} struct X { int i; - ~X(); // non-trivial + ~X(); // non-trivial }; X& g() { @@ -1630,7 +1630,7 @@ { X& px = &g(); px->~X(); - int*p = px->i; // undefined behavior + int*p = px->i; // undefined behavior } \end{codeblock} \end{example} @@ -1965,4 +1965,3 @@ #define __cplusplus 300012L \end{codeblock} \end{example} - From a27ded1ef2b4a2892810c1100abf033b3fd23c4a Mon Sep 17 00:00:00 2001 From: notadragon Date: Mon, 5 May 2025 10:04:53 -0400 Subject: [PATCH 3/7] fixups: , in label id --- source/expressions.tex | 2 +- source/ub.tex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/expressions.tex b/source/expressions.tex index 5c819c1da8..29af397470 100644 --- a/source/expressions.tex +++ b/source/expressions.tex @@ -341,7 +341,7 @@ If a pointer to $X$ would be valid in the context of the evaluation of the expression\iref{basic.fundamental}, the result designates $X$; -otherwise, the behavior is undefined.\ubdef{expr.type.reference,lifetime} +otherwise, the behavior is undefined.\ubdef{expr.type.reference.lifetime} \begin{note} Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see~\ref{basic.life}). diff --git a/source/ub.tex b/source/ub.tex index 5ee0c26067..7056444244 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -489,7 +489,7 @@ \rSec2[ub.expr.type]{Type} \pnum -\ubxref{expr.type.reference,lifetime} \\ +\ubxref{expr.type.reference.lifetime} \\ Evaluating a reference when an equivalent use of a pointer denoting the same object would be invalid has undefined behavior. From 4dd7d1dd74fa399648c7573bbbeca517acbd2237 Mon Sep 17 00:00:00 2001 From: notadragon Date: Mon, 5 May 2025 23:18:00 -0400 Subject: [PATCH 4/7] fixups: cleanups suggested by timur --- source/basic.tex | 2 +- source/classes.tex | 4 ++-- source/expressions.tex | 4 ++-- source/ub.tex | 30 ++++++++++++++++++++++++++---- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index 4cf1138a2b..72eec2efd9 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -5634,7 +5634,7 @@ $P$ is not valid in the context of $E$, then the behavior is undefined if $E$ is an indirection\iref{expr.unary.op} or -an invocation of a deallocation function\iref{basic.stc.dynamic.deallocation}, +an invocation of a deallocation function\iref{basic.stc.dynamic.deallocation}\ubdef{basic.compound.invalid.pointer}, and \impldef{invalid pointer value in the context of an evaluation} otherwise. \begin{footnote} Some implementations might define that diff --git a/source/classes.tex b/source/classes.tex index 2d2d77867f..0e8bf61c1c 100644 --- a/source/classes.tex +++ b/source/classes.tex @@ -6035,10 +6035,10 @@ \indextext{destruction!member access}% For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in -undefined behavior\ubdef{class.cdtor.before.ctor.before.ctor}. +undefined behavior\ubdef{class.cdtor.before.ctor}. 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\ubdef{class.cdtor.before.ctor.after.dtor}. +execution results in undefined behavior\ubdef{class.cdtor.after.dtor}. \begin{example} \begin{codeblock} struct X { int i; }; diff --git a/source/expressions.tex b/source/expressions.tex index 29af397470..fe741e62c5 100644 --- a/source/expressions.tex +++ b/source/expressions.tex @@ -4069,7 +4069,7 @@ If \tcode{E2} is a non-static member and the result of \tcode{E1} is an object whose type is not similar\iref{conv.qual} to the type of \tcode{E1}, -the behavior is undefined\ubdef{expr.ref.not.similar}. +the behavior is undefined\ubdef{expr.ref.member.not.similar}. \begin{example} \begin{codeblock} struct A { int i; }; @@ -6734,7 +6734,7 @@ \pnum For addition or subtraction, if the expressions \tcode{P} or \tcode{Q} have type ``pointer to \cv{}~\tcode{T}'', where \tcode{T} and the array element type -are not similar\iref{conv.qual}, the behavior is undefined\ubdef{expr.add.polymorphic}. +are not similar\iref{conv.qual}, the behavior is undefined\ubdef{expr.add.not.similar}. \begin{example} \begin{codeblock} int arr[5] = {1, 2, 3, 4, 5}; diff --git a/source/ub.tex b/source/ub.tex index 7056444244..f1ff248173 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -305,6 +305,28 @@ \end{codeblock} \end{example} +\rSec2[ub.basic.compound]{Compound types} + +\pnum +\ubxref{basic.compound.invalid.pointer] \\ +Indirection or +the invocation of a deallocation function +with an invalid pointer value +has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +void f() +{ + int *x = new int{5}; + delete x; + int y = *x; // undefined behavior + delete x; // undefined behavior +} +\end{codeblock} +\end{example} + \rSec2[ub.intro.execution]{Sequential execution} @@ -660,7 +682,7 @@ \rSec2[ub.expr.ref]{Class member access} \pnum -\ubxref{expr.ref.not.similar} \\ +\ubxref{expr.ref.member.not.similar} \\ If \tcode{E2} is a non-static member and the result of \tcode{E1} is an object whose type is not similar\iref{conv.qual} to the type of \tcode{E1}, the behavior is undefined. @@ -1053,7 +1075,7 @@ \end{example} \pnum -\ubxref{expr.add.polymorphic} \\ +\ubxref{expr.add.not.similar} \\ For addition or subtraction, if the expressions P or Q have type ``pointer to cv T'', where T and the array element type are not similar \iref{conv.rval}, the behavior is undefined. @@ -1558,7 +1580,7 @@ \rSec2[ub.class.cdtor]{Construction and destruction} \pnum -\ubxref{class.cdtor.before.ctor.before.ctor} \\ +\ubxref{class.cdtor.before.ctor} \\ For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. 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 @@ -1608,7 +1630,7 @@ \pnum -\ubxref{class.cdtor.before.ctor.after.dtor} \\ +\ubxref{class.cdtor.after.dtor} \\ 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 From 156b95316020fbc91c84802673d4e810dd83cf3d Mon Sep 17 00:00:00 2001 From: jberne4 Date: Fri, 9 May 2025 11:35:52 -0400 Subject: [PATCH 5/7] fixups: typo and clarified basic.compound.invalid.pointer --- source/ub.tex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/ub.tex b/source/ub.tex index f1ff248173..d141fe7753 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -311,8 +311,11 @@ \ubxref{basic.compound.invalid.pointer] \\ Indirection or the invocation of a deallocation function -with an invalid pointer value +with a pointer value referencing storage +that has been freed has undefined behavior. +(Most other uses of such a pointer have +implemention-defined behavior.) \pnum \begin{example} @@ -632,7 +635,7 @@ \pnum \ubxref{conv.member.missing.member} \\ -The conversino of +The conversion of a pointer to a member of a base class to a pointer to member of a derived class that coudl not contain that member From 1555db44d0729fcf1cfff7d396d75a9f24a42758 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Fri, 9 May 2025 11:58:26 -0400 Subject: [PATCH 6/7] fixups: split out throwing deallocation function ub --- source/basic.tex | 2 +- source/ub.tex | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index 72eec2efd9..16fffa69a3 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -4423,7 +4423,7 @@ signature. \pnum -If a deallocation function terminates by throwing an exception, the behavior is undefined.\ubxref{basic.stc.alloc.dealloc.constraint.2} +If a deallocation function terminates by throwing an exception, the behavior is undefined.\ubxref{basic.stc.alloc.dealloc.throw} The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect. diff --git a/source/ub.tex b/source/ub.tex index d141fe7753..14ae54d7e0 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -262,11 +262,9 @@ \pnum \ubxref{basic.stc.alloc.dealloc.constraint} \\ -% \ubxref{basic.stc.alloc.dealloc.constraint.2} \\ If the behavior of an allocation or deallocation function does not satisfy the semantic constraints specified -in \iref{basic.stc.dynamic.allocation} and \iref{basic.stc.dynamic.deallocation}, -including exiting via a thrown exception when not specified to do so, +in \iref{basic.stc.dynamic.allocation} and \iref{basic.stc.dynamic.deallocation}. the behavior is undefined. @@ -287,6 +285,27 @@ \end{codeblock} \end{example} +\pnum +\ubxref{basic.stc.alloc.dealloc.throw} \\ +If a call to a deallocation function +terminates by throwing an exception +the behavior is undefined. +\pnum +\begin{example} +\begin{codeblock} +struct X { + void operator delete(void*) { throw "oops"; } +}; +void f() +{ + X* x = new X(); + delete x; // undefined behavior +} +\end{codeblock} +\end{example} + + + \rSec2[ub.basic.stc.alloc.zero.dereference]{Zero-sized allocation dereference} \pnum From d35d61a2499fe90cabc53d8dce0a4c1441b33566 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Fri, 9 May 2025 12:01:36 -0400 Subject: [PATCH 7/7] fixups: ubdef instead of ubxref in basic.tex --- source/basic.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic.tex b/source/basic.tex index 16fffa69a3..e35bfe841b 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -4423,7 +4423,7 @@ signature. \pnum -If a deallocation function terminates by throwing an exception, the behavior is undefined.\ubxref{basic.stc.alloc.dealloc.throw} +If a deallocation function terminates by throwing an exception, the behavior is undefined.\ubdef{basic.stc.alloc.dealloc.throw} The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect.