diff --git a/source/basic.tex b/source/basic.tex index ffa22b82a2..e35bfe841b 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.\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. @@ -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 436d364284..0e8bf61c1c 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}. +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.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..fe741e62c5 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 @@ -4067,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; }; @@ -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 @@ -6731,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/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..14ae54d7e0 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,34 @@ \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. +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. + \pnum \begin{example} @@ -244,6 +285,71 @@ \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 +\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.basic.compound]{Compound types} + +\pnum +\ubxref{basic.compound.invalid.pointer] \\ +Indirection or +the invocation of a deallocation function +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} +\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} \pnum @@ -383,8 +489,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 +512,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 +593,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 +600,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 +624,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 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 +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 @@ -482,7 +704,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. @@ -499,6 +721,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 +780,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 +849,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} @@ -813,7 +1097,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. @@ -1016,6 +1300,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 +1602,7 @@ \rSec2[ub.class.cdtor]{Construction and destruction} \pnum -\ubxref{class.cdtor.before.ctor.after.dtor} \\ +\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 @@ -1316,6 +1651,35 @@ \end{example} +\pnum +\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 +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