diff --git a/source/basic.tex b/source/basic.tex index 733d6699d0..ffa22b82a2 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -614,7 +614,7 @@ \pnum Every program shall contain at least one definition of every function or variable that is odr-used in that program -outside of a discarded statement\iref{stmt.if}; no diagnostic required. +outside of a discarded statement\iref{stmt.if}; no diagnostic required\ifndrdef{basic.def.odr.exact.one.def}. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see~\ref{class.default.ctor}, \ref{class.copy.ctor}, @@ -882,7 +882,7 @@ reachable unnamed enumeration definition in the same scope that have the same first enumerator name and do not have typedef names for linkage purposes\iref{dcl.enum}, -those unnamed enumeration types shall be the same; no diagnostic required. +those unnamed enumeration types shall be the same; no diagnostic required\ifndrdef{basic.def.odr.unnamed.enum.same.type}. \indextext{one-definition rule|)} \rSec1[basic.scope]{Scope}% @@ -1751,7 +1751,7 @@ If it is an invalid set, the program is ill-formed. If it differs from the result of a search in $T$ for $M$ in a complete-class context\iref{class.mem} of $T$, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{class.member.lookup.name.refers.diff.decl}. \begin{example} \begin{codeblock} struct A { int x; }; // S(x,A) = \{ \{ \tcode{A::x} \}, \{ \tcode{A} \} \} @@ -3486,7 +3486,7 @@ zero or more objects of implicit-lifetime types\iref{term.implicit.lifetime.type} in its specified region of storage if doing so would result in the program having defined behavior. -If no such set of objects would give the program defined behavior, +If no such set of objects would give the program defined behavior\ubdef{intro.object.implicit.create}, the behavior of the program is undefined. If multiple such sets of objects would give the program defined behavior, it is unspecified which such set of objects is created. @@ -3748,7 +3748,7 @@ if the pointer were of type \tcode{\keyword{void}*} is well-defined. Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below. The -program has undefined behavior if +program has undefined behavior\ubdef{lifetime.outside.pointer} if: \begin{itemize} \item the pointer is used as the operand of a \grammarterm{delete-expression}, @@ -3811,7 +3811,7 @@ a glvalue refers to allocated storage\iref{basic.stc.dynamic.allocation}, and using the properties of the glvalue that do not depend on its value is -well-defined. The program has undefined behavior if +well-defined. The program has undefined behavior\ubdef{lifetime.outside.glvalue} if: \begin{itemize} \item the glvalue is used to access the object, or \item the glvalue is used to call a non-static member function of the object, or @@ -3901,7 +3901,7 @@ \end{footnote} and another object of the original type does not occupy that same storage location when the implicit destructor call takes -place, the behavior of the program is undefined. This is true +place, the behavior of the program is undefined\ubdef{original.type.implicit.destructor}. This is true even if the block is exited with an exception. \begin{example} \begin{codeblock} @@ -3921,7 +3921,7 @@ Creating a new object within the storage that a const, complete object with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before -its lifetime ended, results in undefined behavior. +its lifetime ended, results in undefined behavior\ubdef{creating.within.const.complete.obj}. \begin{example} \begin{codeblock} struct B { @@ -4260,7 +4260,7 @@ does not satisfy the semantic constraints specified in~\ref{basic.stc.dynamic.allocation} and~\ref{basic.stc.dynamic.deallocation}, -the behavior is undefined. +the behavior is undefined\ubdef{basic.stc.alloc.dealloc.constraint}. \indextext{storage duration!dynamic|)} @@ -6152,7 +6152,7 @@ The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. -The behavior is undefined if +The behavior is undefined\iref{intro.multithread}\ubdef{intro.execution.unsequenced.modification} if \begin{itemize} \item \indextext{side effects}% @@ -6552,7 +6552,7 @@ and neither happens before the other, except for the special case for signal handlers described below. Any such data race results in undefined -behavior. +behavior\ubdef{intro.races.data}. \begin{note} It can be shown that programs that correctly use mutexes and \tcode{memory_order::seq_cst} operations to prevent all data races and use no @@ -6916,7 +6916,7 @@ objects with automatic storage duration\iref{class.dtor}. If \tcode{std::exit} is invoked during the destruction of an object with static or thread storage duration, the program has undefined -behavior. +behavior\ubdef{basic.start.main.exit.during.destruction}. \pnum \indextext{termination!program}% @@ -7210,7 +7210,7 @@ \pnum If a function contains a block variable of static or thread storage duration that has been destroyed and the function is called during the destruction of an object with static or -thread storage duration, the program has undefined behavior if the flow of control +thread storage duration, the program has undefined behavior\ubdef{basic.start.term.use.after.destruction} if the flow of control passes through the definition of the previously destroyed block variable. \begin{note} Likewise, the behavior is undefined @@ -7237,7 +7237,7 @@ handlers\iref{support.runtime} that does not happen before\iref{intro.multithread} completion of destruction of objects with static storage duration and execution of \tcode{std::atexit} registered functions\iref{support.start.term}, the program has -undefined behavior. +undefined behavior\ubdef{basic.start.term.signal.handler}. \begin{note} If there is a use of an object with static storage duration that does not happen before the object's destruction, the program has undefined diff --git a/source/classes.tex b/source/classes.tex index 3a80fc1528..140534569b 100644 --- a/source/classes.tex +++ b/source/classes.tex @@ -2289,7 +2289,7 @@ 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. +undefined behavior\ubdef{class.dtor.not.class.type}. \begin{note} Invoking \keyword{delete} on a null pointer does not call the destructor; see \ref{expr.delete}. @@ -2356,7 +2356,7 @@ \pnum Once a destructor is invoked for an object, the object's lifetime ends; -the behavior is undefined if the destructor is invoked +the behavior is undefined\ubdef{class.dtor.no.longer.exists} if the destructor is invoked for an object whose lifetime has ended\iref{basic.life}. \begin{example} If the destructor for an object with automatic storage duration is explicitly invoked, @@ -3237,7 +3237,7 @@ each anonymous union member \tcode{X}\iref{class.union.anon} that is a member of a union and has such an element as an immediate subobject (recursively), -if modification of \tcode{X} would have undefined behavior under~\ref{basic.life}, +if modification of \tcode{X} would have undefined behavior\ubdef{class.union.assignment.not.start.lifetime} under~\ref{basic.life}, an object of the type of \tcode{X} is implicitly created in the nominated storage; no initialization is performed and @@ -4019,7 +4019,7 @@ \indextext{definition!virtual function}% A virtual function declared in a class shall be defined, or declared pure\iref{class.abstract} in that class, or both; no diagnostic is -required\iref{basic.def.odr}. +required\iref{basic.def.odr}\ifndrdef{class.virtual.pure.or.defined}. \indextext{friend!\tcode{virtual} and}% \pnum @@ -4252,7 +4252,7 @@ \indextext{virtual function call!undefined pure}% the effect of making a virtual call\iref{class.virtual} to a pure virtual function directly or indirectly for the object being created (or -destroyed) from such a constructor (or destructor) is undefined.% +destroyed) from such a constructor (or destructor) is undefined\ubdef{class.abstract.pure.virtual}.% \indextext{derived class|)} \rSec1[class.access]{Member access control}% @@ -5523,7 +5523,7 @@ The target constructor is selected by overload resolution. Once the target constructor returns, the body of the delegating constructor is executed. If a constructor delegates to itself directly or indirectly, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{class.base.init.delegate.itself}. \begin{example} \begin{codeblock} struct C { @@ -5838,7 +5838,7 @@ \item a postcondition assertion of a destructor\iref{dcl.contract.func}, \end{itemize} -the program has undefined behavior. +the program has undefined behavior\ubdef{class.base.init.mem.fun}. \begin{example} \begin{codeblock} class A { @@ -6035,7 +6035,7 @@ \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. For an object with a non-trivial destructor, referring to +undefined behavior\ubdef{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 results in undefined behavior. \begin{example} @@ -6130,7 +6130,7 @@ \tcode{obj} shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member -value) results in undefined behavior. +value) results in undefined behavior\ubdef{class.cdtor.convert.or.form.pointer}. \begin{example} \begin{codeblock} struct A { }; @@ -6174,7 +6174,7 @@ and the object expression refers to the complete object of \tcode{x} or one of that object's base class subobjects but not \tcode{x} or one of its base class subobjects, the behavior -is undefined. +is undefined\ubdef{class.cdtor.virtual.not.x}. \begin{example} \begin{codeblock} struct V { @@ -6231,7 +6231,7 @@ \tcode{typeid} refers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its -bases, the behavior is undefined. +bases, the behavior is undefined\ubdef{class.cdtor.typeid}. \pnum \indextext{construction!dynamic cast and}% @@ -6256,7 +6256,7 @@ the operand is not a pointer to or object of the constructor or destructor's own class or one of its bases, the \keyword{dynamic_cast} -results in undefined behavior. +results in undefined behavior\ubdef{class.cdtor.dynamic.cast}. \begin{example} \begin{codeblock} struct V { diff --git a/source/declarations.tex b/source/declarations.tex index 24b34f254c..d0faff76a8 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -1279,7 +1279,7 @@ \indextext{const object!undefined change to}% Any attempt to modify\iref{expr.assign,expr.post.incr,expr.pre.incr} a const object\iref{basic.type.qualifier} during its -lifetime\iref{basic.life} results in undefined behavior. +lifetime\iref{basic.life} results in undefined behavior\ubdef{dcl.type.cv.modify.const.obj}. \begin{example} \begin{codeblock} const int ci = 3; // cv-qualified (initialized as required) @@ -1323,7 +1323,7 @@ \impldef{semantics of an access through a volatile glvalue}. If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, -the behavior is undefined. +the behavior is undefined\ubdef{dcl.type.cv.access.volatile}. \pnum \indextext{type specifier!\idxcode{volatile}}% @@ -7257,7 +7257,7 @@ The evaluation that invoked a resumption member function is called the \defnx{resumer}{coroutine!resumer}. Invoking a resumption member function for a coroutine -that is not suspended results in undefined behavior. +that is not suspended results in undefined behavior\ubdef{dcl.fct.def.coroutine.resume.not.suspended}. \pnum An implementation may need to allocate additional storage for a coroutine. @@ -7351,7 +7351,7 @@ The storage for the coroutine state is released by calling a non-array deallocation function\iref{basic.stc.dynamic.deallocation}. If \tcode{destroy} is called for a coroutine that is not suspended, the -program has undefined behavior. +program has undefined behavior\ubdef{dcl.fct.def.coroutine.destroy.not.suspended}. \pnum The deallocation function's name is looked up by searching for it in the scope of the promise type. @@ -9464,7 +9464,7 @@ \grammarterm{alignment-specifier}{}, every defining declaration of that entity shall specify an equivalent alignment. -No diagnostic is required if declarations of an entity have +No diagnostic is required\ifndrdef{dcl.align.diff.translation.units} if declarations of an entity have different \grammarterm{alignment-specifier}{s} in different translation units. \begin{example} @@ -9518,7 +9518,7 @@ at the point where the assumption appears, the assumption has no effect. Otherwise, -evaluation of the assumption has runtime-undefined behavior. +evaluation of the assumption has runtime-undefined behavior\ubdef{dcl.attr.assume.false}. \pnum \begin{note} @@ -9948,12 +9948,12 @@ specify the \tcode{noreturn} attribute if any declaration of that function specifies the \tcode{noreturn} attribute. If a function is declared with the \tcode{noreturn} attribute in one translation unit and the same function is declared without the \tcode{noreturn} attribute in another -translation unit, the program is ill-formed, no diagnostic required. +translation unit, the program is ill-formed, no diagnostic required\ifndrdef{dcl.attr.noreturn.trans.unit.mismatch}. \pnum If a function \tcode{f} is invoked where \tcode{f} was previously declared with the \tcode{noreturn} attribute and that invocation eventually returns, -the behavior is runtime-undefined. +the behavior is runtime-undefined\ubdef{dcl.attr.noreturn.eventually.returns}. \begin{note} The function can terminate by throwing an exception. diff --git a/source/exceptions.tex b/source/exceptions.tex index 79684e4c51..5d5a4282e4 100644 --- a/source/exceptions.tex +++ b/source/exceptions.tex @@ -690,7 +690,7 @@ Referring to any non-static member or base class of an object in the handler for a \grammarterm{function-try-block} -of a constructor or destructor for that object results in undefined behavior. +of a constructor or destructor for that object results in undefined behavior\ubdef{except.handle.handler.ctor.dtor}. \pnum Exceptions thrown in destructors of objects with static storage duration or in diff --git a/source/expressions.tex b/source/expressions.tex index aed3d0b9dc..b24b248021 100644 --- a/source/expressions.tex +++ b/source/expressions.tex @@ -64,7 +64,7 @@ \indextext{zero!remainder undefined}% If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for -its type, the behavior is undefined. +its type, the behavior is undefined\ubdef{expr.expr.eval}. \begin{note} \indextext{overflow}% Treatment of division by zero, forming a remainder using a zero divisor, @@ -313,7 +313,7 @@ If a program attempts to access\iref{defns.access} the stored value of an object through a glvalue through which it is not type-accessible, -the behavior is undefined. +the behavior is undefined\ubdef{expr.basic.lvalue.strict.aliasing.violation}. \begin{footnote} The intent of this list is to specify those circumstances in which an object can or cannot be aliased. @@ -983,7 +983,7 @@ that exact representation. If the source value is between two adjacent destination values, 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. +Otherwise, the behavior is undefined\ubdef{conv.double.out.of.range}. \pnum The conversions allowed as floating-point promotions are excluded from @@ -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 if the truncated value cannot be represented +The behavior is undefined\ubdef{conv.fpint.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}. @@ -3227,7 +3227,7 @@ \end{note} If the substitution of template arguments into a \grammarterm{requirement} would always result in a substitution failure, the program is ill-formed; -no diagnostic required. +no diagnostic required\ifndrdef{expr.prim.req.always.sub.fail}. \begin{example} \begin{codeblock} template concept C = @@ -3597,7 +3597,7 @@ expression whose function type is not call-compatible with the type of the called function's -definition results in undefined behavior. +definition results in undefined behavior\ubdef{expr.call.different.type}. \begin{note} This requirement allows the case when the expression has the type of a @@ -4067,7 +4067,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. +the behavior is undefined\ubdef{expr.ref.not.similar}. \begin{example} \begin{codeblock} struct A { int i; }; @@ -4409,7 +4409,7 @@ type ``\cvqual{cv1} \tcode{B}''. If the object of type ``\cvqual{cv1} \tcode{B}'' is actually a base class subobject of an object of type \tcode{D}, the result refers to the enclosing object of type -\tcode{D}. Otherwise, the behavior is undefined. +\tcode{D}. Otherwise, the behavior is undefined\ubdef{expr.static.cast.base.class}. \begin{example} \begin{codeblock} struct B { }; @@ -4534,7 +4534,7 @@ the value is unchanged if the original value is within the range of the enumeration values\iref{dcl.enum}, and -otherwise, the behavior is undefined. +otherwise, the behavior is undefined\ubdef{expr.static.cast.enum.outside.range}. A value of floating-point type can also be explicitly converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration\iref{conv.fpint}, and subsequently to @@ -4549,7 +4549,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. +Otherwise, the behavior is undefined\ubdef{expr.static.cast.downcast.wrong.derived.type}. \pnum \indextext{cast!base class}% @@ -4593,7 +4593,7 @@ member pointer value of the destination type. If class \tcode{B} contains the original member, or is a base class of the class containing the original member, the resulting pointer to member points -to the original member. Otherwise, the behavior is undefined. +to the original member. Otherwise, the behavior is undefined\ubdef{expr.static.cast.does.not.contain.orignal.member}. \begin{note} Although class \tcode{B} need not contain the original member, the dynamic type of the object with which indirection through the pointer @@ -5968,7 +5968,7 @@ \end{note} If the allocation function is a non-allocating form\iref{new.delete.placement} that returns null, -the behavior is undefined. +the behavior is undefined\ubdef{expr.new.non.allocating.null}. Otherwise, if the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of @@ -6140,7 +6140,7 @@ that resulted from a previous non-array \grammarterm{new-expression}, or a pointer to a base class subobject of an object created by such a \grammarterm{new-expression}. -If not, the behavior is undefined. +If not, the behavior is undefined\ubdef{expr.delete.mismatch}. \indextext{array!\idxcode{delete}}% In an array delete expression, the value of the operand of \keyword{delete} may be a null pointer value or a pointer value that resulted from @@ -6173,9 +6173,9 @@ is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall -have a virtual destructor or the behavior is undefined. In an array delete +have a virtual destructor or the behavior is undefined\ubdef{expr.delete.dynamic.type.differ}. In an array delete expression, if the dynamic type of the object to be deleted is not similar to -its static type, the behavior is undefined. +its static type, the behavior is undefined\ubdef{expr.delete.dynamic.array.dynamic.type.differ}. \pnum \indextext{type!incomplete}% @@ -6514,7 +6514,7 @@ whose type is not similar to the type of \tcode{E1}, or whose most derived object does not contain the member to which -\tcode{E2} refers, the behavior is undefined. +\tcode{E2} refers, the behavior is undefined\ubdef{expr.mptr.oper.not.contain.member}. The expression \tcode{E1} is sequenced before the expression \tcode{E2}. \pnum @@ -6566,7 +6566,7 @@ operand is an lvalue and an xvalue otherwise. The result of a \tcode{.*} expression whose second operand is a pointer to a member function is a prvalue. If the second operand is the null -member pointer value\iref{conv.mem}, the behavior is undefined. +member pointer value\iref{conv.mem}, the behavior is undefined\ubdef{expr.mptr.oper.member.func.null}. \rSec2[expr.mul]{Multiplicative operators}% \indextext{expression!multiplicative operators}% @@ -6607,7 +6607,7 @@ expression by the second. \indextext{zero!undefined division by}% If the second operand of \tcode{/} or \tcode{\%} is zero, the behavior is -undefined. +undefined\ubdef{expr.mul.div.by.zero}. For integral operands, the \tcode{/} operator yields the algebraic quotient with any fractional part discarded; \begin{footnote} @@ -6615,7 +6615,7 @@ \end{footnote} if the quotient \tcode{a/b} is representable in the type of the result, \tcode{(a/b)*b + a\%b} is equal to \tcode{a}; otherwise, the behavior -of both \tcode{a/b} and \tcode{a\%b} is undefined. +of both \tcode{a/b} and \tcode{a\%b} is undefined\ubdef{expr.mul.representable.type.result}. \rSec2[expr.add]{Additive operators}% \indextext{expression!additive operators}% @@ -6694,7 +6694,7 @@ and the expression \tcode{P - J} points to the (possibly-hypothetical) array element $i - j$ of \tcode{x} if $0 \le i - j \le n$. -\item Otherwise, the behavior is undefined. +\item Otherwise, the behavior is undefined\ubdef{expr.add.out.of.bounds}. \end{itemize} \begin{note} Adding a value other than $0$ or $1$ @@ -6725,13 +6725,13 @@ of type \tcode{std::ptrdiff_t}, the behavior is undefined\iref{expr.pre}. \end{note} -\item Otherwise, the behavior is undefined. +\item Otherwise, the behavior is undefined\ubdef{expr.add.sub.diff.pointers}. \end{itemize} \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. +are not similar\iref{conv.qual}, the behavior is undefined\ubdef{expr.add.polymorphic}. \begin{example} \begin{codeblock} int arr[5] = {1, 2, 3, 4, 5}; @@ -6768,7 +6768,7 @@ promotions are performed. The type of the result is that of the promoted left operand. \indextext{left shift!undefined}% -The behavior is undefined if the right operand is negative, or greater +The behavior is undefined\ubdef{expr.shift.neg.and.width} if the right operand is negative, or greater than or equal to the width of the promoted left operand. \pnum @@ -7665,7 +7665,7 @@ If the value being stored in an object is read via another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is -undefined. +undefined\ubdef{expr.assign.overlap}. \begin{note} This restriction applies to the relationship between the left and right sides of the assignment operation; it is not a diff --git a/source/ifndr.tex b/source/ifndr.tex new file mode 100644 index 0000000000..4bdc7199e2 --- /dev/null +++ b/source/ifndr.tex @@ -0,0 +1,595 @@ +%!TEX root = std.tex +\infannex{ifndr}{Enumeration of Ill-formed, No Diagnostic Required} + +This Annex documents ill-formed no diagnostic required behavior called out in the main standard text by +the following phrases: no diagnostic is required, no diagnostic required and no diagnostic shall be issued. +Each entry contains a title, a numeric cross reference to the main standard text, a summary of the issue +and a code example demonstrating the issue. The code examples are there to clarify the ill-formed no +diagnostic required cases and will not exhaustively cover all possible ways of invoking that case. + +\rSec1[ifndr.lex]{\ref{lex}: Lexical conventions} + +\rSec2[ifndr.lex.name]{Identifiers} + +\pnum +\ifndrxref{lex.name.reserved} \\ +Some identifiers are reserved for use by \Cpp{} implementations and shall not be used otherwise; no +diagnostic is required. + +\pnum +\begin{example} +\begin{codeblock} +int _z; // no diagnostic required, \tcode{\_z} is reserved because it starts with \tcode{\_} at global scope + +int main() { + int __x; // no diagnostic required, \tcode{\_\_x} is reserved because it starts with \tcode{\_\_} + int _Y; // no diagnostic required, \tcode{\_Y} is reserved because it starts with \tcode{\_} followed by a capital letter + int x__y; // no diagnostic required, \tcode{x\_\_y} is reserved because it contains \tcode{\_\_} +} +\end{codeblock} +\end{example} + + +\rSec1[ifndr.basic]{\ref{basic}: Basics} + +\rSec2[ifndr.basic.def.odr]{One-definition rule} + +\pnum +\ifndrxref{basic.def.odr.exact.one.def} \\ +Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in +that program outside of a discarded statement \iref{stmt.if}; no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +auto f() { + struct A {}; + return A{}; +} +decltype(f()) g(); +auto x = g(); // ill-formed, no diagnostic required, function \tcode{g} is used but not defined + // in this translation unit, and cannot be defined in any other translation unit + // because its type does not have linkage +\end{codeblock} +\end{example} + + +\pnum +\ifndrxref{basic.def.odr.unnamed.enum.same.type} \\ +If, at any point in the program, there is more than one reachable unnamed enumeration definition in the same scope that have +the same first enumerator name and do not have typedef names for linkage purposes \iref{dcl.enum}, those unnamed enumeration +types shall be the same; no diagnostic required. + + +\pnum +\begin{example} +\begin{codeblock} +// a.h +enum { a }; + +// b.h +enum { a, b }; + +// main.cpp +import "a.h"; +import "b.h"; +auto n = decltype(a)::b; // ill-formed no diagnostic required, more than one unnammed enum + // definition reachable at this point but their types are not the same +\end{codeblock} +\end{example} + + +\rSec2[ifndr.class.member.lookup]{Member name lookup} + +\pnum +\ifndrxref{class.member.lookup.name.refers.diff.decl} \\ +A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. + +\pnum +\begin{example} +\begin{codeblock} +struct foo {}; + +struct bar { + foo *m_foo; + + foo *foo() { + return m_foo; + } // IFNDR, foo now refers to member function foo() while previously referred to struct foo +}; +\end{codeblock} +\end{example} +\begin{example} +\begin{codeblock} +struct B { + static int f(); +}; + +struct D : public B { + using B::f; + int g(decltype(f()) x) { + return 0; + } // Ill-formed no diagnostic required, + // decltype(f()) will refer to B::f() here but if + // moved to the end of D would refer to D::f() + static float f(); +}; + +int main() { + D d; + + return d.g(0); +} +\end{codeblock} +\end{example} + + +\rSec1[ifndr.expr]{\ref{expr}: Expressions} + +\rSec2[ifndr.expr.prim.req]{Requires expressions} + +\pnum +\ifndrxref{expr.prim.req.always.sub.fail} \\ +If the substitution of template arguments into a \grammarterm{requirement} +would always result in a substitution failure, the program is ill-formed; no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +template concept C = requires { + new int[-(int)sizeof(T)]; // ill-formed, no diagnostic required, the size of the allocation is + // required to be greater than zero but can never be +}; +\end{codeblock} +\end{example} + + +\rSec1[ifndr.stmt.stmt]{\ref{stmt}: Statements} + +\rSec2[ifndr.stmt.ambig]{Ambiguity resolution} + +\pnum +\ifndrxref{stmt.ambig.bound.diff.parse} \\ +If, during +parsing, a name in a template parameter is bound differently than it would be bound during a trial parse, +the program is ill-formed. No diagnostic is required. + +\pnum +\begin{example} +\begin{codeblock} +template struct A { const static int a = 20; }; + +template <> struct A<100> { using a = char; }; + +const int x = 10; + +int main() { + using T = const int; + T(x) + (100), (y)(A::a); // ill-formed no diagnostic required, during trial parse the template + // parameter \tcode{x} is bound to the global \tcode{x} later during parsing the template + // parameter \tcode{x} is bound to the local \tcode{x} declared on the same line +} +\end{codeblock} +\end{example} + + +\rSec1[ifndr.dcl.dcl]{\ref{dcl}: Declarations} + +\rSec2[ifndr.dcl.align]{Alignment specifier} + +\pnum +\ifndrxref{dcl.align.diff.translation.units} \\ +No diagnostic is required if declarations of an entity have different \grammarterm{alignment-specifier}s in different +translation units. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +struct S { int x; } s, *p = &s; +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +struct alignas(16) S; // ill-formed, no diagnostic required, definition of \tcode{S} lacks alignment +extern S* p; +\end{codeblocktu} +\end{example} + +\rSec2[ifndr.dcl.attr.noreturn]{Noreturn attribute} + +\pnum +\ifndrxref{dcl.attr.noreturn.trans.unit.mismatch} \\ + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +[[noreturn]] void f() {} +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +void f(int i); // ill-formed no diagnostic required, declared without \tcode{noreturn} +\end{codeblocktu} +\end{example} + +\rSec1[ifndr.module]{\ref{module}: Modules} + +\rSec2[ifndr.module.unit]{Module units and purviews} + +\pnum +\ifndrxref{module.unit.reserved.identifiers} \\ +All \grammarterm{module-name}s either beginning with an identifier consisting of +std followed by zero or more digits or containing a reserved identifier\iref{lex.token} are reserved and shall not be +specified in a \grammarterm{module-declaration}; no diagnostic is required. + +\pnum +\begin{example} +\begin{codeblock} +module std; // ill-formed no diagnostic required, \tcode{std} is not allowed at the beginning +module module; // ill-formed no diagnostic required, \tcode{module} is a reserved identifier +module std0; // ill-formed no diagnostic required, \tcode{std} followed by digits is not allowed at the beginning +export module _Test; // ill-formed no diagnostic required, \tcode{_Test} is a reserved identifier +export module te__st; // ill-formed no diagnostic required, \tcode{te__st} is a reserved identifier +\end{codeblock} +\end{example} + + +\pnum +\ifndrxref{module.unit.named.module.no.partition} \\ +A named module shall contain exactly one module interface +unit with no module-partition, known as the primary module interface unit of the module; no diagnostic is +required. + +\pnum +\begin{example} +\begin{codeblock} +module A; +export import :Internals; // ill-formed no diagnostic required, module partition not allowed +\end{codeblock} +\end{example} + + +\rSec1[ifndr.class]{\ref{class}: Classes} + +\rSec2[ifndr.class.base.init]{Initializing bases and members} + +\pnum +\ifndrxref{class.base.init.delegate.itself} \\ +If a constructor delegates to itself directly or indirectly, +the program is ill-formed, no diagnostic required + +\pnum +\begin{example} +\begin{codeblock} +struct C { + C( int ) { } // \#1: non-delegating constructor + C(): C(42) { } // \#2: delegates to \#1 + C( char c ) : C(42.0) { } // \#3: ill-formed no diagnostic required due to recursion with \#4 + C( double d ) : C('a') { } // \#4: ill-formed no diagnostic required due to recursion with \#3 +}; +\end{codeblock} +\end{example} + + +\rSec2[ifndr.class.virtual]{Virtual functions} + +\pnum +\ifndrxref{class.virtual.pure.or.defined} \\ +A virtual function must be declared pure or defined, no diagnostic required. A virtual function declared pure can be defined +out of line + +\pnum +\begin{example} +\begin{codeblock} +class A { + virtual void f(); +}; + +int main() { + A a; // ill-formed no diagnostic required, virtual function that is not pure but has not definition +} +\end{codeblock} +\end{example} + + +\rSec1[ifndr.over]{\ref{over}: Overloading} + +\rSec2[ifndr.over.literal]{User-defined literals} + +\pnum +\ifndrxref{over.literal.reserved} \\ +Some literal suffix identifiers are +reserved for future standardization. A declaration whose literal-operator-id uses such a literal +suffix identifier is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +float operator ""E(const char*); // ill-formed, no diagnostic required, reserved literal suffix +double operator"" _Bq(long double); // ill-formed, no diagnostic required, // uses the reserved identifier \tcode{_Bq} +\end{codeblock} +\end{example} + +\rSec1[ifndr.temp]{\ref{temp}: Templates} + +\rSec2[ifndr.temp.pre]{Preamble} + +\pnum +\ifndrxref{temp.pre.reach.def} \\ +A definition of a function template, member function of a class template, variable template, or static data +member of a class template shall be reachable from the end of every definition domain\iref{basic.def.odr} in which it is +implicitly instantiated\iref{temp.inst} unless the corresponding specialization is explicitly instantiated\iref{temp.explicit} in +some translation unit; no diagnostic is required. + +\pnum +\begin{example} +\begin{codeblock} +// a.h +template +void f(); + +// a.cpp +#include "a.h" +int main() { + f(); // ill-formed no diagnostic required, function template implicity + // instantiated but not reachable definition +} +\end{codeblock} +\end{example} + + +\rSec2[ifndr.temp.arg.template]{Template template arguments} + +\pnum +\ifndrxref{temp.arg.template.sat.constraints} \\ +Any partial specializations\iref{temp.spec.partial} associated with the primary template are considered when a specialization +based on the template template-parameter is instantiated. If a specialization is not reachable from the point of +instantiation, and it would have been selected had it been reachable, the program is ill-formed, no diagnostic +required. + +\pnum +\begin{example} +\begin{codeblock} +template struct A { + int x; +}; + +template class V> struct C { + V y; + V z; +}; + +C c; + +// Ill-formed no diagnostic required, specialization is not reachable from point +// of instantiation above and it would have been selected if it had +template struct A { + long x; +}; +\end{codeblock} +\end{example} + + +\rSec2[ifndr.constr.atomic]{Atomic constraints} + +\pnum +\ifndrxref{temp.constr.atomic.equiv.but.not.equiv} \\ +If the validity or meaning of the program depends on whether two atomic +constraints are equivalent, +and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +template void f2() +requires Add1<2 * N>; +template int f2() +requires Add1 && true; +void h2() { +f2<0>(); // ill-formed, no diagnostic required, + // requires determination of subsumption between atomic constraints that are + // functionally equivalent but not equivalent +\end{codeblock} +\end{example} + +\pnum +\ifndrxref{temp.constr.atomic.sat.result.diff} \\ +If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +template +concept Complete = sizeof(T) == sizeof(T); + +struct A; +static_assert(!Complete); // \#1 +struct A {}; +static_assert(Complete); // ill-formed no diagnostic required, satisfaction + // result differs from point \#1 +\end{codeblock} +\end{example} + + +\rSec2[ifndr.temp.constr.normal]{Constraint normalization} + +\pnum +\ifndrxref{temp.constr.normal.invalid} \\ +If during constraint normalization any such substitution results in an invalid type or expression, +the program is ill-formed; no diagnostic is required + +\pnum +\begin{example} +\begin{codeblock} +template concept A = T::value || true; +template concept B = A; +template concept C = B; // Ill-formed no diagnostic required, it + // would form the invalid type V\&* in the parameter mapping +\end{codeblock} +\end{example} + + +\rSec2[ifndr.temp.spec.partial]{Partial specialization} + +\pnum +\ifndrxref{temp.spec.partial.general.partial.reachable} \\ +A partial specialization shall be reachable from any use of a template specialization that would make use of the +partial specialization as the result of an implicit or explicit instantiation; no diagnostic is required. + +\pnum +\begin{example} +\begin{codeblock} +template class X{ +public: + void foo(){}; +}; + +template class X; // ill-formed no diagnostic required, explicit instantiation + // and partial specialization is not reachable + +template class X{ +public: + void baz(); +}; +\end{codeblock} +\end{example} + + +\rSec2[ifndr.temp.names]{Names of template specializations} + +\pnum +\ifndrxref{temp.names.sat.constraints} \\ +When the template-name of a simple-template-id names a constrained non-function template or a constrained +template template-parameter, and all template-arguments in the simple-template-id are non-dependent\iref{temp.dep.temp}, +the associated constraints\iref{temp.constr.decl} of the constrained template shall be satisfied\iref{temp.constr.constr}. + +\pnum +\begin{example} +\begin{codeblock} +template concept C1 = sizeof(T) != sizeof(int); +template using Ptr = T*; + +Ptr p; // error: constraints not satisfied + +template +struct S2 { Ptr x; }; // ill-formed, no diagnostic required +\end{codeblock} +\end{example} + + +\rSec2[ifndr.temp.fct]{Function templates} + +\rSec3[ifndr.temp.over.link]{Function template overloading} + +\pnum +\ifndrxref{temp.over.link.equiv.not.equiv} \\ +If the validity or meaning of the program depends on whether two constructs are equivalent, and they are +functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +template +struct A{}; + +// ill-formed, no diagnostic required, the following declarations are functionally equivalent but not equivalent +template void f(A, A); +template void f(A, A); +\end{codeblock} +\end{example} + +\rSec2[ifndr.temp.res]{Name resolution} + +\rSec3[ifndr.temp.res.general]{General} + +\pnum +\ifndrxref{temp.res.general.default.but.not.found} \\ +If the validity or meaning of the program would be changed by considering a default argument or default +template argument introduced in a declaration that is reachable from the point of instantiation of a +specialization\iref{temp.point} but is not found by lookup for the specialization, the program is ill-formed, no +diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +\end{codeblock} +\end{example} + +\rSec2[ifndr.temp.dep.res]{Dependent name resolution} + +\rSec3[ifndr.temp.point]{Point of instantiation} + +\pnum +\ifndrxref{temp.point.diff.pt.diff.meaning} \\ +A specialization for a class template has at most one point of instantiation within a translation unit. A +specialization for any template may have points of instantiation in multiple translation units. If two different +points of instantiation give a template specialization different meanings according to the one-definition +rule (6.3), the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +// a.h +#include + +template +struct is_complete : std::false_type {}; + +template +struct is_complete> : std::true_type {}; + +// a.cpp +#include "a.h" +struct X; +static_assert(!is_complete::value); + +// b.cpp +#include "a.h" +struct X { }; +static_assert(is_complete::value); +\end{codeblock} +\end{example} + + +\rSec2[ifndr.temp.explicit]{Explicit instantiation} + +\pnum +\ifndrxref{temp.explicit.decl.implicit.inst} \\ +An entity that is the subject of +an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit +instantiation\iref{temp.inst} in the translation unit shall be the subject of an explicit instantiation definition +somewhere in the program; otherwise the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +// Explicit instantiation declaration +extern template class std::vector; + +int main() { + std::cout << std::vector().size(); // ill-formed no diagnostic required, implicit instantiation + // but no explicit instantiation definition +} +\end{codeblock} +\end{example} + + +\rSec2[ifndr.temp.deduct]{Template argument deduction} + +\rSec3[ifndr.temp.deduct.general]{General} + +\pnum +\ifndrxref{temp.deduct.general.diff.order} \\ +If substitution +into different declarations of the same function template would cause template instantiations to occur in a +different order or not at all, the program is ill-formed; no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +template typename T::X h(typename A::X); +template auto h(typename A::X) -> typename T::X; // redeclaration +template void h(...) { } + +void x() { + h(0); // ill-formed, no diagnostic required +} +\end{codeblock} +\end{example} diff --git a/source/lex.tex b/source/lex.tex index 6cf011e432..254fb4b341 100644 --- a/source/lex.tex +++ b/source/lex.tex @@ -941,7 +941,7 @@ appearing as a \grammarterm{token} or \grammarterm{preprocessing-token} are reserved for use by \Cpp{} implementations and shall -not be used otherwise; no diagnostic is required. +not be used otherwise; no diagnostic is required \ifndrdef{lex.name.reserved}. \begin{itemize} \item Each identifier that contains a double underscore diff --git a/source/macros.tex b/source/macros.tex index ec88684fcd..7e3cf1429a 100644 --- a/source/macros.tex +++ b/source/macros.tex @@ -380,6 +380,17 @@ \newcommand{\ctype}{\Fundesc{Type}} \newcommand{\templalias}{\Fundesc{Alias template}} +%% Undefined behavior and ill-formed, no diagnostic required +% Use in main body of text to mark undefined behavior +\newcommand{\ubdef}[1]{\nolinebreak[3] (\label{ub:#1}\ref{ubx:#1})} +% Use in Annex to cross-reference marked undefined behavior +\newcommand{\ubxref}[1]{\label{ubx:#1}\textbf{Specified in:}\space\ref{ub:#1}\newline} + +% Use in main body of text to mark IFNDR +\newcommand{\ifndrdef}[1]{\nolinebreak[3] (\label{ifndr:#1}\ref{ifndrx:#1})} +% Use in Annex to cross-reference marked IFNDR +\newcommand{\ifndrxref}[1]{\label{ifndrx:#1}\textbf{Specified in:}\space\ref{ifndr:#1}} + %% Cross-reference \newcommand{\xref}[1]{\textsc{See also:}\space #1} \newcommand{\xrefc}[1]{\xref{\IsoC{}, #1}} diff --git a/source/modules.tex b/source/modules.tex index a0386b38ca..ce158e0738 100644 --- a/source/modules.tex +++ b/source/modules.tex @@ -39,7 +39,7 @@ consisting of \tcode{std} followed by zero or more \grammarterm{digit}{s} or containing a reserved identifier\iref{lex.name} are reserved and shall not be specified in a \grammarterm{module-declaration}; -no diagnostic is required. +no diagnostic is required\ifndrdef{module.unit.reserved.identifiers}. If any \grammarterm{identifier} in a reserved \grammarterm{module-name} is a reserved identifier, the module name is reserved for use by \Cpp{} implementations; @@ -54,7 +54,7 @@ A named module shall contain exactly one module interface unit with no \grammarterm{module-partition}, known as the \defn{primary module interface unit} of the module; -no diagnostic is required. +no diagnostic is required\ifndrdef{module.unit.named.module.no.partition}. \pnum A \defn{module partition} is diff --git a/source/overloading.tex b/source/overloading.tex index 5343a015a7..077f75696f 100644 --- a/source/overloading.tex +++ b/source/overloading.tex @@ -4078,7 +4078,7 @@ The first form of \grammarterm{literal-operator-id} is deprecated\iref{depr.lit}. Some literal suffix identifiers are reserved for future standardization; see~\ref{usrlit.suffix}. A declaration whose \grammarterm{literal-operator-id} uses -such a literal suffix identifier is ill-formed, no diagnostic required. +such a literal suffix identifier is ill-formed, no diagnostic required\ifndrdef{over.literal.reserved}. \pnum A declaration whose \grammarterm{declarator-id} is a diff --git a/source/preprocessor.tex b/source/preprocessor.tex index ee7959abeb..2d49317505 100644 --- a/source/preprocessor.tex +++ b/source/preprocessor.tex @@ -532,7 +532,7 @@ \tcode{defined} unary operator does not match one of the two specified forms prior to macro replacement, -the behavior is undefined. +the behavior is undefined\ubdef{cpp.cond.defined}. \pnum After all replacements due to macro expansion and @@ -747,7 +747,7 @@ replacement list of preprocessing tokens). If the directive resulting after all replacements does not match one of the two previous forms, the behavior is -undefined. +undefined\ubdef{cpp.include.one.of.two.forms}. \begin{note} Adjacent \grammarterm{string-literal}s are not concatenated into a single \grammarterm{string-literal} @@ -1589,7 +1589,7 @@ \begin{footnote} A \grammarterm{conditionally-supported-directive} is a preprocessing directive regardless of whether the implementation supports it. \end{footnote} -the behavior is undefined. +the behavior is undefined\ubdef{cpp.replace.macro.pptoken}. \pnum \begin{example} @@ -1795,7 +1795,7 @@ \tcode{"} characters). If the replacement that results is not a valid character string literal, -the behavior is undefined. The character string literal corresponding to +the behavior is undefined\ubdef{cpp.stringize.invalid.char}. The character string literal corresponding to an empty stringizing argument is \tcode{""}. The order of evaluation of \tcode{\#} @@ -1844,7 +1844,7 @@ a \grammarterm{universal-character-name}\iref{lex.charset}. \end{note} If the result is not a valid preprocessing token, -the behavior is undefined. +the behavior is undefined\ubdef{cpp.concat.invalid.preprocessing.token}. The resulting preprocessing token is available for further macro replacement. The order of evaluation of \tcode{\#\#} @@ -2055,7 +2055,7 @@ by the digit sequence (interpreted as a decimal integer). If the digit sequence specifies zero or a number greater than 2147483647, -the behavior is undefined. +the behavior is undefined\ubdef{cpp.line.zero.or.overflow}. \pnum A preprocessing directive of the form @@ -2079,7 +2079,7 @@ (each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens). If the directive resulting after all replacements does not match -one of the two previous forms, the behavior is undefined; +one of the two previous forms, the behavior is undefined\ubdef{cpp.line.pptoken.not.match}; otherwise, the result is processed as appropriate. \rSec1[cpp.error]{Diagnostic directives}% @@ -2420,7 +2420,7 @@ or a \tcode{\#undef} preprocessing directive, -the behavior is undefined. +the behavior is undefined\ubdef{cpp.predefined.define.undef}. Any other predefined macro names shall begin with a leading underscore followed by an uppercase letter or a second underscore. diff --git a/source/statements.tex b/source/statements.tex index ba75c744d2..7b403c5d01 100644 --- a/source/statements.tex +++ b/source/statements.tex @@ -940,7 +940,7 @@ equivalent to a \tcode{return} with no operand. Otherwise, flowing off the end of a function that is neither \tcode{main}\iref{basic.start.main} nor a coroutine\iref{dcl.fct.def.coroutine} -results in undefined behavior. +results in undefined behavior\ubdef{stmt.return.flow.off}. \pnum The copy-initialization of the result of the call is sequenced before the @@ -1032,7 +1032,7 @@ flowing off the end of a coroutine's \grammarterm{function-body} is equivalent to a \keyword{co_return} with no operand; otherwise flowing off the end of a coroutine's \grammarterm{function-body} -results in undefined behavior. +results in undefined behavior\ubdef{stmt.return.coroutine.flow.off}. \rSec2[stmt.goto]{The \keyword{goto} statement}% \indextext{statement!\idxcode{goto}} @@ -1171,7 +1171,7 @@ \end{note} If control re-enters the declaration recursively while -the variable is being initialized, the behavior is undefined. +the variable is being initialized, the behavior is undefined\ubdef{stmt.dcl.local.static.init.recursive}. \begin{example} \begin{codeblock} int foo(int i) { @@ -1263,7 +1263,7 @@ that a name in a template argument is bound to (part of) the declaration being parsed, the program is ill-formed. -No diagnostic is required. +No diagnostic is required\ifndrdef{stmt.ambig.bound.diff.parse}. \begin{example} \begin{codeblock} struct T1 { diff --git a/source/std.tex b/source/std.tex index 0170e31451..d3e0ab8c4e 100644 --- a/source/std.tex +++ b/source/std.tex @@ -165,6 +165,8 @@ \include{limits} \include{compatibility} \include{future} +\include{ub} +\include{ifndr} \include{uax31} %%-------------------------------------------------- diff --git a/source/templates.tex b/source/templates.tex index bf283e46da..28b42fb1f6 100644 --- a/source/templates.tex +++ b/source/templates.tex @@ -225,7 +225,7 @@ shall be reachable from the end of every definition domain\iref{basic.def.odr} in which it is implicitly instantiated\iref{temp.inst} unless the corresponding specialization is explicitly instantiated\iref{temp.explicit} -in some translation unit; no diagnostic is required. +in some translation unit; no diagnostic is required\ifndrdef{temp.pre.reach.def}. \rSec1[temp.param]{Template parameters} @@ -904,7 +904,7 @@ are non-dependent\iref{temp.dep.temp}, the associated constraints\iref{temp.constr.decl} of the constrained template -shall be satisfied\iref{temp.constr.constr}. +shall be satisfied\iref{temp.constr.constr}\ifndrdef{temp.names.sat.constraints}. \begin{example} \begin{codeblock} template concept C1 = sizeof(T) != sizeof(int); @@ -1401,7 +1401,7 @@ specialization based on the template template parameter is instantiated. If a specialization is not reachable from the point of instantiation, and it would have been selected had it been reachable, the program is ill-formed, -no diagnostic required. +no diagnostic required\ifndrdef{temp.arg.template.sat.constraints}. \begin{example} \begin{codeblock} template class A { // primary template @@ -1758,7 +1758,7 @@ if the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{temp.constr.atomic.equiv.but.not.equiv}. \begin{example} \begin{codeblock} template void f2() @@ -1789,7 +1789,7 @@ results in \tcode{true}. If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{temp.constr.atomic.sat.result.diff}. \begin{example} \begin{codeblock} template concept C = @@ -2143,7 +2143,7 @@ after substituting in \tcode{E$_i$} the respective $i^\text{th}$ concept argument of each \tcode{P$_k$}. If any such substitution results in an invalid type or expression, - the program is ill-formed; no diagnostic is required. + the program is ill-formed; no diagnostic is required\ifndrdef{temp.constr.normal.invalid}. \item Otherwise, the normal form of \tcode{F} is @@ -3612,7 +3612,7 @@ that template. A partial specialization shall be reachable from any use of a template specialization that would make use of the partial specialization as the result of -an implicit or explicit instantiation; no diagnostic is required. +an implicit or explicit instantiation; no diagnostic is required\ifndrdef{temp.spec.partial.general.partial.reachable}. \pnum Two partial specialization declarations declare the same entity @@ -4222,7 +4222,7 @@ If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, -no diagnostic required. +no diagnostic required\ifndrdef{temp.over.link.equiv.not.equiv}. Furthermore, if two declarations $A$ and $B$ of function templates \begin{itemize} \item @@ -4777,7 +4777,7 @@ introduced in a declaration that is reachable from the point of instantiation of a specialization\iref{temp.point} but is not found by lookup for the specialization, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{temp.res.general.default.but.not.found}. \begin{bnf} \nontermdef{typename-specifier}\br @@ -5971,7 +5971,7 @@ translation units. If two different points of instantiation give a template specialization different meanings according to the one-definition rule\iref{basic.def.odr}, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{temp.point.diff.pt.diff.meaning}. \rSec3[temp.dep.candidate]{Candidate functions} @@ -6645,7 +6645,7 @@ There is an \impldef{maximum depth of recursive template instantiations} quantity that specifies the limit on the total depth of recursive instantiations\iref{implimits}, which could involve more than one template. -The result of an infinite recursion in instantiation is undefined. +The result of an infinite recursion in instantiation is undefined\ubdef{temp.inst.inf.recursion}. \begin{example} \begin{codeblock} template class X { @@ -6874,7 +6874,7 @@ in a way that would otherwise cause an implicit instantiation\iref{temp.inst} in the translation unit shall be the subject of an explicit instantiation definition somewhere in the -program; otherwise the program is ill-formed, no diagnostic required. +program; otherwise the program is ill-formed, no diagnostic requiredi\ifndrdef{temp.explicit.decl.implicit.inst}. \begin{note} This rule does apply to inline functions even though an explicit instantiation declaration of such an entity has no other normative @@ -7716,7 +7716,7 @@ a condition that causes deduction to fail is encountered. If substitution into different declarations of the same function template would cause template instantiations to occur in a different order or not at all, -the program is ill-formed; no diagnostic required. +the program is ill-formed; no diagnostic required\ifndrdef{temp.deduct.general.diff.order}. \begin{note} The equivalent substitution in exception specifications\iref{except.spec} diff --git a/source/ub.tex b/source/ub.tex new file mode 100644 index 0000000000..030bdaed4e --- /dev/null +++ b/source/ub.tex @@ -0,0 +1,1720 @@ +%!TEX root = std.tex +\infannex{ub}{Enumeration of Core Undefined Behavior} + +This Annex documents undefined behavior explicitly called out in the main standard text using the +following phrases: the behavior of the program is undefined, has undefined behavior, results in +undefined behavior, the behavior is undefined, have undefined behavior, is undefined, result has +undefined behavior. Undefined behavior that is implicit is not covered by this annex. Each entry contains +a title, a numeric cross reference to the main standard text, a summary of the issue and a code example +demonstrating the issue. The code examples are there to clarify the undefined behavior and will not +exhaustively cover all possible ways of invoking that case. + +\rSec1[ub.basic]{\ref{basic}: Basics} + +\rSec2[ub.intro.object]{Implicitly creating object and undefined behavior} + +\pnum +\ubxref{intro.object.implicit.create} +For each +operation that is specified as implicitly creating objects, that operation implicitly creates and starts the +lifetime of zero or more objects of implicit-lifetime types \iref{basic.types} in its specified region of storage if doing so +would result in the program having defined behavior. If no such set of objects would give the program defined +behavior, the behavior of the program is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#include +struct X { + int a, b; + ~X() = delete; // deleted destructor makes \tcode{X} a non-implicit-lifetime class +}; + +X* make_x() { + // The call to std::malloc can not implicitly create an object of type X + // because X is not an implicit-lifetime class. + X* p = (X*)std::malloc(sizeof(struct X)); + p->a = 1; // undefined behavior, no set of objects give us defined behavior + return p; +} +\end{codeblock} +\end{example} +\begin{example} +\begin{codeblock} +#include + +struct X { + int a; +}; + +struct Y { + int x; +}; + +*make_y() { + X* p1 = (X*)std::malloc(sizeof(struct X)); + p1->a = 1; + Y* p2 = (Y*)p1; + p2->x = 2; // undefined behavior, lifetime of \tcode{p1} was started but not + // ended and lifetime of \tcode{p2} was not started. + return p2; +} +\end{codeblock} +\end{example} + +\rSec2[ub.basic.life]{Object lifetime} + +\pnum +\ubxref{lifetime.outside.pointer} \\ +The behavior of some uses of a pointer +pointing to an object outside its lifetime +are not defined: +\begin{itemize} +\item + the object will be or was of a class type with a non-trivial destructor + and the pointer is used as the operand of a \grammarterm{delete-expression}, +\item + the pointer is used to access a non-static data member or call a + non-static member function of the object, or +\item + the pointer is implicitly converted\iref{conv.ptr} to a pointer + to a virtual base class, or +\item + the pointer is used as the operand of a + \tcode{static_cast}\iref{expr.static.cast}, except when the conversion + is to pointer to \cv{}~\tcode{void}, or to pointer to \cv{}~\tcode{void} + and subsequently to pointer to + \cv{}~\tcode{char}, + \cv{}~\tcode{unsigned char}, or + \cv{}~\tcode{std::byte}\iref{cstddef.syn}, or +\item + the pointer is used as the operand of a + \tcode{dynamic_cast}\iref{expr.dynamic.cast}. +\end{itemize} + +\pnum +\begin{example} +\begin{codeblock} +struct S { + float f = 0; + ~S() {} +}; + +float f() { + S s; + S* p1 = &s; + S* p2 = new S; + s.~S(); + p2->~S(); + delete p2; // undefined behavior, operand of delete, lifetime has ended and \tcode{S} + // has a non-trivial destructor + return p1->f; // Undefined behavior, accessing non-static data member after + // end of lifetime +} +\end{codeblock} +\end{example} +\begin{example} +\begin{codeblock} +#include +#include + +struct B { + virtual void f(); + void mutate(); + virtual ~B(); +}; + +struct D1 : B { + void f(); +}; +struct D2 : B { + void f(); +}; + +void B::mutate() { + new (this) D2; // reuses storage and ends the lifetime of *this + f(); // undefined behavior + B *b = this; // OK, this points to valid memory +} + +void g() { + void* p = std::malloc(sizeof(D1) + sizeof(D2)); + B* pb = new (p) D1; + pb->mutate(); + *pb; // OK, pb points to valid memory + void* q = pb; // OK, pb points to valid memory + pb->f(); // undefined behavior, lifetime of \tcode{*pb} has ended +} +\end{codeblock} +\end{example} + +\pnum +\ubxref{lifetime.outside.glvalue} \\ +The behavior of some uses of a glvalue +that refers to an object outside its lifetime +are not defined. + +\pnum +\begin{example} +\begin{codeblock} +struct A{void f(){} }; + +void f() { + int x = int{10}; + A a; + using T = int; + x.~T(); + a.~A(); + a.f(); // undefined behavior, glvalue used to access a + // non-static member function after the lifetime has ended + int y = x; // undefined behavior, glvalue used to access the + // object after the lifetime has ended +} +\end{codeblock} +\end{example} + + +\pnum +\ubxref{original.type.implicit.destructor} \\ +The behavior of an implicit destructor call when the type that is not +the original type occupies the storage. + +\pnum +\begin{example} +\begin{codeblock} +class T {}; + +struct B { + ~B(); +}; + +void h() { + B b; + new (&b) T; +} // undefined behavior at block exit +\end{codeblock} +\end{example} + + +\pnum +\ubxref{creating.within.const.complete.obj} \\ +Creating a new object within the storage that a const complete object with static, thread, or automatic +storage duration occupies, or within the storage that such a const object used to occupy before its lifetime +ended, results in undefined behavior + +\pnum +\begin{example} +\begin{codeblock} +struct B { + B(); + ~B(); +}; + +const B b; + +void h() { + b.~B(); + new (const_cast(&b)) const B; // 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. + +\pnum +\begin{example} +\begin{codeblock} +#include +void* operator new(std::size_t sz) { + if (sz == 0) + return nullptr; // undefined behavior, should return non-null pointer + + return std::malloc(1); // undefined behavior, if successful should return allocation with + // length in bytes is at least as large as the requested size +} +void operator delete(void* ptr) noexcept { + throw 0; // undefined behavior, terminates by throwing an exception +} +\end{codeblock} +\end{example} + +\rSec2[ub.intro.execution]{Sequential execution} + +\pnum +\ubxref{intro.execution.unsequenced.modification} \\ +If a side effect on a +memory location \iref{intro.memory} is unsequenced relative to either another side effect on the same memory location or +a value computation using the value of any object in the same memory location, and they are not potentially +concurrent \iref{intro.multithread}, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +void g(int i) { + i = 7, i++, i++; // i becomes 9 + + i = i++ + 1; // the value of i is incremented + i = i++ + i; // undefined behavior + i = i + 1; // the value of i is incremented +} +\end{codeblock} +\end{example} + +\rSec2[ub.intro.races]{Data races} + +\pnum +\ubxref{intro.races.data} \\ +The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, +at least one of which is not atomic, and neither happens before the other, except for the special case for +signal handlers described in \iref{intro.races}. Any such data race results in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +int count = 0; +auto f = [&] { count++; }; +std::thread t1{f}, t2{f}, t3{f}; +// Undefined behavior t1, t2 and t3 have a data race on access of variable count +\end{codeblock} +\end{example} + + +\rSec2[ub.basic.start.main]{main function} + +\pnum +\ubxref{basic.start.main.exit.during.destruction} \\ +If std::exit is called to +end a program during the destruction of an object with static or thread storage duration, the program has +undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +#include + +struct Exiter { + ~Exiter() { std::exit(0); } +}; + +Exiter ex; // + +int main() {} +// Undefined behavior when destructor of static variable ex is called it will call std::exit +\end{codeblock} +\end{example} + + +\rSec2[ub.basic.start.term]{Termination} + +\pnum +\ubxref{basic.start.term.use.after.destruction} \\ +If a function contains a block-scope object of static or thread storage duration that has been destroyed and the +function is called during the destruction of an object with static or thread storage duration, the program has +undefined behavior if the flow of control passes through the definition of the previously destroyed block-scope +object. Likewise, the behavior is undefined if the block-scope object is used indirectly (i.e., through a pointer) +after its destruction. + +\pnum +\begin{example} +\begin{codeblock} +struct A {}; +void f() { + static A a; +} + +struct B { + B() { f(); } +}; +struct C { + ~C() { f(); } +}; + +C c; +B b; + +int main() {} +// Undefined behavior, static objects are destructed in reverse order, in this case a then b and +// finally c. When the destructor of c is call it calls f() which passes through definition of +// previously destroyed block-scope object +\end{codeblock} +\end{example} + + +\pnum +\ubxref{basic.start.term.signal.handler} \\ +If there is a use of a standard library object or function not permitted within signal handlers \iref{support.runtime} that +does not happen before \iref{intro.multithread} completion of destruction of objects with static storage duration and execution +of std::atexit registered functions \iref{support.start.term}, the program has undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +\end{codeblock} +\end{example} + + +\rSec1[ub.expr]{\ref{expr}: Expressions} +\rSec2[ub.expr.eval]{Result of Expression not Mathematically Defined/out of Range} + +\pnum +\ubxref{expr.expr.eval} \\ +If during the evaluation of an expression, the result is not mathematically defined or not in the range of +representable values for its type, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#include +int main() { + // Assuming 32-bit int the range of values are: -2,147,483,648 to 2,147,483,647 + int x1 = std::numeric_limits::max() + 1; // undefined behavior, 2,147,483,647 + 1 is not representable as an int + int x2 = std::numeric_limits::min() / -1; // undefined behavior, -2,147,483,648 / -1 is not representable as an int +} +\end{codeblock} +\end{example} + +\rSec2[ub.basic.lval]{Value category} + +\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 + +\pnum +\begin{example} +\begin{codeblock} +int foo(float* f, int* i) { + *i = 1; + *f = 0.f; // undefined behavior, glvalue is not similar to \tcode{int} + + return *i; +} + +int main() { + int x = 0; + + x = foo(reinterpret_cast(&x), &x); +} +\end{codeblock} +\end{example} + + +\rSec2[ub.conv.double]{Floating-point conversions} + +\pnum +\ubxref{conv.double.out.of.range} \\ +Converting a floating point value to a type that cannot represent the value is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +#include + +int main() { + // Assuming 32-bit int, 32-bit float and 64-bit double + double d2 = std::numeric_limits::max(); + float f = d2; // undefined behavior on systems where the range of + // representable values of float is [-max,+max] on system where + // 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} + + +\rSec2[ub.conv.fpint]{Floating-integral conversions} + +\pnum +\ubxref{conv.fpint.not.represented} \\ +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. + +\pnum +\begin{example} +\begin{codeblock} +#include + +int main() { + // Assuming 32-bit int the range of values are: -2,147,483,648 to + // 2,147,483,647 Assuming 32-bit float and 64-bit double + double d = (double)std::numeric_limits::max() + 1; + int x1 = d; // undefined behavior 2,147,483,647 + 1 is not representable as int + + __uint128_t x2 = -1; + float f = x2; // undefined behavior on systems where the range of + // representable values of float is [-max,+max] on system where + // represetable values are [-inf,+inf] this would not be UB +} +\end{codeblock} +\end{example} + +\rSec2[ub.expr.call]{Function call} + +\pnum +\ubxref{expr.call.different.type} \\ +Calling a function through an expression whose function type is different from the function type of the called +function's definition results in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +using f_float = int (*)(float); +using f_int = int (*)(int); + +int f1(float) { return 10; } + +int f2(int) { return 20; } + +int main() { + return reinterpret_cast(f1)(20); // undefined behavior, the function type of the expression + // is different from the called functions definition +} +\end{codeblock} +\end{example} + +\rSec2[ub.expr.ref]{Class member access} + +\pnum +\ubxref{expr.ref.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. + +\pnum +\begin{example} +\begin{codeblock} +struct A { int i; }; +struct B { int j; }; +struct D : A, B {}; +void f() { + D d; + reinterpret_cast(d).j; // undefined behavior +} +\end{codeblock} +\end{example} + + +\rSec2[ub.expr.static.cast]{Static cast} + +\pnum +\ubxref{expr.static.cast.base.class} \\ +We can cast a base class B to a reference to derived class D (with certain restrictions wrt to cv qualifiers) +as long B is a base class subobject of type D, otherwise the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +truct B {}; +struct D1 : B {}; +struct D2 : B {}; + +void f() { + D1 d; + B &b = d; + static_cast(b); // undefined behavior, base class object of type \tcode{D1} not \tcode{D2} +} +\end{codeblock} +\end{example} + +\pnum +\ubxref{expr.static.cast.enum.outside.range} \\ +If the enumeration type does not have a fixed underlying +type, the value is unchanged if the original value is within the range of the enumeration values \iref{dcl.enum}, and +otherwise, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +enum A { e1 = 1, e2 }; + +void f() { + enum A a = static_cast(4); // undefined behavior, 4 is not with the range of enumeration values +} +\end{codeblock} +\end{example} + +\pnum +\ubxref{expr.static.cast.downcast.wrong.derived.type} \\ +Down-casting to the wrong derived type is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct B {}; +struct D1 : B {}; +struct D2 : B {}; + +void f() { + B *bp = new D1; + static_cast(bp); // undefined behavior, base class object of type \tcode{D1} not \tcode{D2} +} +\end{codeblock} +\end{example} + +\pnum +\ubxref{expr.static.cast.does.not.contain.orignal.member} \\ +We can cast a pointer to mamber of dervied class D to a pointer to memeber of base class D (with certain restrictions wrt to cv qualifiers) +as long B contains the original member, is a base or derived class of the class containing the original member, otherwise the behavior is undefined. + + +\pnum +\begin{example} +\begin{codeblock} +struct A { + char c; +}; + +struct B { + int i; +}; + +struct D : A, B {}; + +int main() { + char D::*p1 = &D::c; + char B::*p2 = static_cast(p1); // undefined behavior, \tcode{B} does not contain the original member \tcode{c} +} +\end{codeblock} +\end{example} + + +\rSec2[ub.expr.new]{New} + +\pnum +\ubxref{expr.new.non.allocating.null} \\ +If the allocation +function is a non-allocating form \iref{new.delete.placement} that returns null, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#include +[[nodiscard]] void* operator new(std::size_t size, void* ptr) noexcept { + return nullptr; // undefined behavior, should return non-null pointer +} +\end{codeblock} +\end{example} +\begin{example} +\begin{codeblock} +#include + +struct A { + int x; +}; + +int main() { + char *p = nullptr; + A *a = new (p) A; // undefined behavior, non-allocting new returning nullptr +} +\end{codeblock} +\end{example} + + +\rSec2[ub.expr.delete]{Delete} + +\pnum +\ubxref{expr.delete.mismatch} \\ +Using array delete on the result of a single object new expression and vice versa is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +int* x = new int; +delete[] x; // undefined behavior, allocated using single object new expression +\end{codeblock} +\end{example} + + +\pnum +\ubxref{expr.delete.dynamic.type.differ} \\ +If the static type of the object to be deleted is different from its dynamic +type and the selected deallocation function (see below) is not a destroying operator delete, the static type +shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual +destructor or the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +struct B { + int a; +}; + +struct D : public B { + int b; +}; + +void f() { + B* b = new D; + delete b; // undefined behavior, no virtual destructor +} +\end{codeblock} +\end{example} + + +\pnum +\ubxref{expr.delete.dynamic.array.dynamic.type.differ} \\ +In an array delete expression, if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +struct B { + virtual ~B(); + void operator delete[](void*, std::size_t); +}; + +struct D : B { + void operator delete[](void*, std::size_t); +}; + +void f(int i) { + D* dp = new D[i]; + delete[] dp; // uses \tcode{D::operator delete[](void*, std::size_t)} + B* bp = new D[i]; + delete[] bp; // undefined behavior +} +\end{codeblock} +\end{example} + + +\rSec2[ub.expr.mptr.oper]{Pointer-to-member operators} + +\pnum +\ubxref{expr.mptr.oper.not.contain.member} \\ +Abbreviating \grammarterm{pm-expression}.*\grammarterm{cast-expression} as \tcode{E1.*E2}, \tcode{E1} is called the object expression. If the dynamic type +of \tcode{E1} does not contain the member to which \tcode{E2} refers, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +struct B {}; +struct D : B { + int x; +}; + +void f() { + B *b = new B; + D *d = static_cast(b); + int D::*p = &D::x; + (*d).*p = 1; // undefined behavior, dynamic type \tcode{B} does not contain \tcode{x} +} +\end{codeblock} +\end{example} + + +\pnum +\ubxref{expr.mptr.oper.member.func.null} \\ +If the second operand is the null +member pointer value \iref{conv.mem}, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +struct S { + int f(); +}; + +void f() { + S cs; + int (S::*pm)() = nullptr; + (cs.*pm)(); // undefined behavior, the second operand is null +} +\end{codeblock} +\end{example} + + +\rSec2[ub.expr.mul]{Multiplicative operators} + +\pnum +\ubxref{expr.mul.div.by.zero} \\ +Division by zero is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +int main() { + int x = 1 / 0; // undefined behavior, division by zero + double d = 1.0 / 0.0; // undefined behavior on systems where the range of + // representable values of float is [-max,+max] on system where + // represetable values are [-inf,+inf] this would not be UB +} +\end{codeblock} +\end{example} + +\pnum +\ubxref{expr.mul.representable.type.result} \\ +If the +quotient a/b is representable in the type of the result, (a/b)*b + a\%b is equal to a; otherwise, the behavior +of both a/b and a\%b is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#include + +int main() { + int x = + std::numeric_limits::min() / -1; // Assuming LP64 -2147483648 which when divided by -1 + // gives us 2147483648 which is not representable by int +} +\end{codeblock} +\end{example} + +\rSec2[ub.expr.add]{Additive operators} + +\pnum +\ubxref{expr.add.out.of.bounds} \\ +Creating an out of bounds pointer is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +static const int arrs[10]{}; + +int main() { + const int *y = arrs + 11; // undefined behavior, creating an out of bounds pointer +} +\end{codeblock} +\end{example} + +\begin{example} +\begin{codeblock} +static const int arrs[10][10]{}; + +int main() { + const int(*y)[10] = arrs + 11; // undefined behavior, creating an out of bounds pointer. + // We can't treat arrs as-if it was a pointer to 100 int, +} +\end{codeblock} +\end{example} + +\pnum +\ubxref{expr.add.sub.diff.pointers} \\ +Subtracting pointers that are not part of the same array is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +#include +void f() { + int x[2]; + int y[2]; + int* p1 = x; + int* p2 = y; + std::ptrdiff_t off = p1 - p2; // undefined behavior, \tcode{p1} and \tcode{p2} point to different arrays +} +\end{codeblock} +\end{example} + +\pnum +\ubxref{expr.add.polymorphic} \\ +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. + +\pnum +\begin{example} +\begin{codeblock} +struct S { + int i; +}; + +struct T : S { + double d; +}; + +void f(const S* s, std::size_t count) { + for (const S* end = s + count; s != end; ++s) { + ... + } +} + +int main() { + T test[5]; + f(test, 5); +} +\end{codeblock} +\end{example} + + +\rSec2[ub.expr.shift]{Shift operators} + +\pnum +\ubxref{expr.shift.neg.and.width} \\ +Shifting by a negative amount or equal or greater than the bit-width of a type is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +int y = 1 << -1; // undefined behavior, shift is negative + +static_assert(sizeof(int) == 4 && CHAR_BIT == 8); +int y1 = 1 << 32; // undefined behavior, shift is equal to the bit width of int +int y2 = 1 >> 32; // undefined behavior, shift is equal to the bit width of int +\end{codeblock} +\end{example} + + +\rSec2[ub.expr.assign]{Assignment and compound assignment operators} + +\pnum +\ubxref{expr.assign.overlap} \\ +Overlap in the storage between the source and destination may result in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +int x = 1; +char* c = reinterpret_cast(&x); +x = *c; // undefined behavior, source overlaps storage of destination +\end{codeblock} +\end{example} + +\rSec1[ub.stmt.stmt]{\ref{stmt}: Statements} + +\rSec2[ub.stmt.return]{The return statement} + +\pnum +\ubxref{stmt.return.flow.off} \\ +Flowing off the end of a function other +than main or a coroutine results in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +int f(int x) { + if (x) + return 1; + // undefined behavior if \tcode{x} is \tcode{0} +} + +void b() { + int x = f(0); // undefined behavior, using 0 as an argument will cause f(...) to flow off the end + // with a return statement +} +\end{codeblock} +\end{example} + +\rSec2[ub.return.coroutine]{The co_return statement} + +\pnum +\ubxref{stmt.return.coroutine.flow.off} \\ +Falling off the end of a coroutine function body that does not return void is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +#include +#include +#include +#include + +class resumable { + public: + struct promise_type; + using coro_handle = std::coroutine_handle; + resumable(coro_handle handle) : handle_(handle) { assert(handle); } + resumable(resumable&) = delete; + resumable(resumable&&) = delete; + bool resume() { + if (not handle_.done()) + handle_.resume(); + return not handle_.done(); + } + ~resumable() { handle_.destroy(); } + const char* return_val(); + + private: + coro_handle handle_; +}; + +struct resumable::promise_type { + using coro_handle = std::experimental::coroutine_handle; + const char* string_; + auto get_return_object() { return coro_handle::from_promise(*this); } + auto initial_suspend() { return std::experimental::suspend_always(); } + auto final_suspend() noexcept { return std::experimental::suspend_always(); } + void unhandled_exception() { std::terminate(); } + void return_value(const char* string) { string_ = string; } +}; + +const char* resumable::return_val() { + return handle_.promise().string_; +} + +resumable foo() { + std::cout << "Hello" << std::endl; + co_await std::experimental::suspend_always(); + // undefined behavior, falling off the end of coroutine that does not return void +} + +int main() { + resumable res = foo(); + while (res.resume()) + ; + std::cout << res.return_val() << std::endl; +} +\end{codeblock} +\end{example} + +\rSec2[ub.stmt.dcl]{Declaration statement} + +\pnum +\ubxref{stmt.dcl.local.static.init.recursive} \\ +If control re-enters the declaration recursively while the +variable is being initialized, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +int foo(int i) { + static int s = foo(2 * i); // recursive call - undefined + return i + 1; +} +\end{codeblock} +\end{example} + + + + +\rSec1[ub.dcl.dcl]{\ref{dcl}: Declarations} + +\rSec2[ub.dcl.type.cv]{The cv-qualifiers} + +\pnum +\ubxref{dcl.type.cv.modify.const.obj} \\ +Any attempt to modify a const object during its lifetime results in +undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +const int* ciq = new const int(3); // initialized as required +int* iq = const_cast(ciq); // cast required +*iq = 4; // undefined: modifies a const object +\end{codeblock} +\end{example} + + +\pnum +\ubxref{dcl.type.cv.access.volatile} \\ +If an attempt is made to +access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior +is undefined + +\pnum +\begin{example} +\begin{codeblock} +volatile int x = 0; +int& y = const_cast(x); +std::cout << y; // undefined behavior, accessing volatile through non-volatile glvalue +\end{codeblock} +\end{example} + + +\rSec2[ub.dcl.fct.def.coroutine]{Coroutine definitions} + +\pnum +\ubxref{dcl.fct.def.coroutine.resume.not.suspended} \\ +Invoking a resumption member function for a coroutine that is not suspended results in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +#include + +using namespace std::experimental; + +struct minig { + struct promise_type { + int val; + minig get_return_object() { return {*this}; } + constexpr suspend_always initial_suspend() noexcept { return {}; } + constexpr suspend_always final_suspend() noexcept { return {}; } + constexpr void return_void() noexcept {} + [[noreturn]] void unhandled_exception() noexcept { throw; } + suspend_always yield_value(int v) noexcept { + val = v; + return {}; + } + }; + using HDL = coroutine_handle; + HDL coro; + minig(promise_type& p) : coro(HDL::from_promise(p)) {} + ~minig() { coro.destroy(); } + bool move_next() { + coro.resume(); + return !coro.done(); + } + int current_value() { return coro.promise().val; } +}; + +static minig f(int n) { + for (int i = 0; i < n; ++i) + co_yield i; +} + +int main() { + auto g = f(10); + int sum = 0; + while (g.move_next()) + sum += g.current_value(); +#if 0 + // undefined behavior to call move_next(), because coro.resume() is + // UB after final_suspend returns, even when it returns + // suspend_always + g.move_next(); +#endif + return sum; +} +\end{codeblock} +\end{example} + + +\pnum +\ubxref{dcl.fct.def.coroutine.destroy.not.suspended} \\ +Invoking destroy() on a coroutine that is not suspended is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +#include + +using namespace std::experimental; + +struct minig { + struct promise_type { + int val; + minig get_return_object() { return {*this}; } + constexpr suspend_always initial_suspend() noexcept { return {}; } + constexpr suspend_always final_suspend() noexcept { return {}; } + constexpr void return_void() noexcept {} + [[noreturn]] void unhandled_exception() { throw; } + suspend_always yield_value(int v) noexcept { + val = v; + return {}; + } + }; + using HDL = coroutine_handle; + HDL coro; + minig(promise_type& p) : coro(HDL::from_promise(p)) {} + ~minig() { coro.destroy(); } + bool move_next() { + coro.resume(); + return !coro.done(); + } + int current_value() { + // this coroutine is not suspended therefore a call to destroy is undefined + // behaviour + coro.destroy(); + return coro.promise().val; + } +}; + +static minig f(int n) { + for (int i = 0; i < n; ++i) + co_yield i; +} + +int main() { + auto g = f(10); + int sum = 0; + while (g.move_next()) + sum += g.current_value(); + + return sum; +} +\end{codeblock} +\end{example} + +\rSec2[ub.dcl.attr.assume]{Assumption attribute} + +\pnum +\ubxref{dcl.attr.assume.false} \\ +If am assumption expression would not evaluate to true at the point where it +appears the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +int g(int x) { + [[assume(x >= 0)]]; + return x/32; +} + +int f() { + return g(-10); // undefined behavior, assumption in \tcode{g} is \tcode{false} +} +\end{codeblock} +\end{example} + + +\rSec2[ub.dcl.attr.noreturn]{Noreturn attribute} + +\pnum +\ubxref{dcl.attr.noreturn.eventually.returns} \\ +If a function f is called where f was previously declared with the \tcode{noreturn} attribute and f eventually returns, +the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +[[noreturn]] void f(int i) { + if (i > 0) + throw "positive"; +} + +int main() { + f(0); // undefined behavior, returns from a [[noreturn]] function +} +\end{codeblock} +\end{example} + + +\rSec1[ub.class]{\ref{class}: Classes} + +\rSec2[ub.class.dtor]{Destructors} + +\pnum +\ubxref{class.dtor.not.class.type} \\ +The invocation of a destructor is subject to the usual rules for +member functions \iref{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. + +\pnum +\begin{example} +\begin{codeblock} +#include + +struct X {}; + +void f() { + X *x = nullptr; + x->~X(); // undefined behavior, invoked using a null pointer value +} + +struct Y {}; +void h() { + alignas(X) char buf[sizeof(X)]; + X *p = new (buf) X(); // use \tcode{buf[] }and initialize + + Y *yp = reinterpret_cast(p); + yp->Y::~Y(); // undefined behavior, destructor of \tcode{Y} called for object of type \tcode{X} +} + +struct Base { + virtual ~Base(); +}; +struct Derived : Base {}; + +int k() { + Base *b = new Base; + Derived *d = static_cast(b); + d->~Derived(); // undefined behavior, destructor of Derived call for object of + // type \tcode{Base} +} +\end{codeblock} +\end{example} + +\pnum +\ubxref{class.dtor.no.longer.exists} \\ +Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the +destructor is invoked for an object whose lifetime has ended. + +\pnum +\begin{example} +\begin{codeblock} +struct A { + ~A() {} +}; + +int main() { + A a; + a.~A(); // undefined behavior, destructor will be invoked again at scope exit +} +\end{codeblock} +\end{example} + + +\rSec2[ub.class.union]{Unions} + +\pnum +\ubxref{class.union.assignment.not.start.lifetime} \\ +Assigning to a union member may not start its lifetime, in that case using it will result in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct X { + const int a; + int b; +}; + +union Y { + X x; + int k; +}; + +void g() { + Y y = {{1, 2}}; // OK, \tcode{y.x} is active union member\iref{class.mem} + int n = y.x.a; + y.k = 4; // OK, ends lifetime of \tcode{y.x}, \tcode{y.k} is active member of union + y.x.b = n; // undefined behavior: \tcode{y.x.b} modified outside its lifetime, + // \tcode{S(y.x.b)} is empty because \tcode{X}'s default constructor is deleted, + // so union member \tcode{y.x}'s lifetime does not implicitly start +} +\end{codeblock} +\end{example} + + +\rSec2[ub.class.abstract]{Abstract classes} + +\pnum +\ubxref{class.abstract.pure.virtual} \\ +Calling a pure virtual function from a constructor or destructor in an abstract class is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct B { + virtual void f() = 0; + B() { + f(); // undefined behavior, \tcode{f} is pure virtual and we are calling from the constructor + } +}; + +struct D : B { + void f() override; +}; +\end{codeblock} +\end{example} + + +\rSec2[ub.class.base.init]{Initializing bases and members} + +\pnum +\ubxref{class.base.init.mem.fun} \\ +It is undefined behavior to call a member function before all the \grammarterm{mem-initializer}s for base classes have completed. + +\pnum +\begin{example} +\begin{codeblock} +class A { +public: + A(int); +}; + +class B : public A { + int j; + +public: + int f(); + B() + : A(f()), // undefined: calls member function but base Ac not yet initialized + j(f()) {} // well-defined: bases are all initialized +}; + +class C { +public: + C(int); +}; + +class D : public B, C { + int i; + +public: + D() + : C(f()), // undefined: calls member function but base \tcode{C} not yet initialized + i(f()) {} // well-defined: bases are all initialized +}; +\end{codeblock} +\end{example} + + +\rSec2[ub.class.cdtor]{Construction and destruction} + +\pnum +\ubxref{class.cdtor.before.ctor.after.dtor} \\ +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 +results in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct X { + int i; +}; +struct Y : X { + Y(); +}; // non-trivial +struct A { + int a; +}; +struct B : public A { + int j; + Y y; +}; // non-trivial + +extern B bobj; +B *pb = &bobj; // OK +int *p1 = &bobj.a; // undefined, refers to base class member +int *p2 = &bobj.y.i; // undefined, refers to member's member + +A *pa = &bobj; // undefined, upcast to a base class type +B bobj; // definition of \tcode{bobj} + +extern X xobj; +int *p3 = &xobj.i; // OK, \tcode{X} is a trivial class +X xobj; + +struct W { + int j; +}; +struct X : public virtual W {}; +struct Y { + int *p; + X x; + Y() : p(&x.j) { // undefined, \tcode{x} is not yet constructed + } +}; +\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 +member of a class, construction must have started and destruction must not have finished otherwise +this is undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct A {}; +struct B : virtual A {}; +struct C : B {}; +struct D : virtual A { + D(A *); +}; +struct X { + X(A *); +}; +struct E : C, D, X { + E() + : D(this), // undefined: upcast from \tcode{E*} to \tcode{A*} might use path E->D->A + // but \tcode{D} is not constructed + // \tcode{D((C*)this)} would be defined: E->C is defined because + // \tcode{E()} has started, and C->A is defined because \tcode{C} is fully + // constructed + X(this) // well-defined: upon construction of \tcode{X}, C/B/D/A sublattice is fully constructed + {} +}; +\end{codeblock} +\end{example} + +\pnum +\ubxref{class.cdtor.virtual.not.x} \\ +When a virtual function is called directly or indirectly from a constructor or from a destructor, +including during the construction or destruction of the class's non-static data members, and the object to +which the call applies is the object (call it \tcode{x}) under construction or destruction, the function called is the +final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class. +If the virtual function call uses an explicit class member access \iref{expr.ref} and the object expression refers +to the complete object of \tcode{x} or one of that object's base class subobjects but not \tcode{x} or one of its base class +subobjects, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +struct V { + virtual void f(); + virtual void g(); +}; + +struct A : virtual V { + virtual void f(); +}; + +struct B : virtual V { + virtual void g(); + B(V *, A *); +}; + +struct D : A, B { + virtual void f(); + virtual void g(); + D() : B((A *)this, this) {} +}; + +B::B(V *v, A *a) { + f(); // calls \tcode{V::f}, not \tcode{A::f} + g(); // calls \tcode{B::g}, not \tcode{D::g} + v->g(); // \tcode{v} is base of \tcode{B}, the call is well-defined, calls \tcode{B::g} + a->f(); // undefined behavior, \tcode{a}'s type not a base of \tcode{B} +} +\end{codeblock} +\end{example} + + +\pnum +\ubxref{class.cdtor.typeid} \\ +If the operand of \tcode{typeid} refers to +the object under construction or destruction and the static type of the operand is neither the constructor or +destructor's class nor one of its bases, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +struct V { + virtual void f(); +}; + +struct A : virtual V {}; +struct B : virtual V { + B(V *, A *); +}; + +struct D : A, B { + D() : B((A *)this, this) {} +}; + +B::B(V *v, A *a) { + typeid(*this); // \tcode{std::type_info} for \tcode{B} + typeid(*v); // well-defined: \tcode{*v} has type \tcode{V}, a base of \tcode{B} yields \tcode{std::type_info} for \tcode{B} + typeid(*a); // undefined behavior: type \tcode{A} not a base of \tcode{B} +} +\end{codeblock} +\end{example} + + +\pnum +\ubxref{class.cdtor.dynamic.cast} \\ +If the operand of the +\tcode{dynamic_cast} refers to the object under construction or destruction and the static type of the operand is +not a pointer to or object of the constructor or destructor's own class or one of its bases, the \tcode{dynamic_cast} +results in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +struct V { + virtual void f(); +}; + +struct A : virtual V {}; +struct B : virtual V { + B(V *, A *); +}; + +struct D : A, B { + D() : B((A *)this, this) {} +}; + +B::B(V *v, A *a) { + dynamic_cast(v); // well-defined: \tcode{v} of type \tcode{V*}, \tcode{V} base of \tcode{B} results in \tcode{B*} + dynamic_cast(a); // undefined behavior: \tcode{a} has type \tcode{A*}, \tcode{A} not a base of \tcode{B} +} +\end{codeblock} +\end{example} + + +\rSec1[ub.temp]{\ref{temp}: Templates} +\rSec2[ub.temp.inst]{Implicit instantiation} + +\pnum +\ubxref{temp.inst.inf.recursion} \\ +The result of an infinite recursion in template instantiation is undefined. + +\pnum +\begin{example} +\begin{codeblock} +template +class X { + X *p; // OK + X a; // implicit instantiation of \tcode{X} requires + // the implicit instantiation of \tcode{X} which requires + // the implicit instantiation of \tcode{X} which \ldots +}; + +int main() { + X x; // undefined behavior, instantiation will kick off infinite recursion +} +\end{codeblock} +\end{example} + + +\rSec1[ub.except]{\ref{except}: Exception handling} + +\rSec2[ub.except.handle]{Handling an exception} + +\pnum +\ubxref{except.handle.handler.ctor.dtor} \\ +Referring to any non-static member or base class of an object in the handler for a \grammarterm{function-try-block} of a +constructor or destructor for that object results in undefined behavior. + +\pnum +\begin{example} +\begin{codeblock} +#include + +struct A { + A() try : x(0 ? 1 : throw 1) { + } catch (int) { + std::cout << "y: " << y << std::endl; // Undefined behavior, referring to non-static member y in + // the handler of function-try-block + } + int x; + int y = 42; +}; +\end{codeblock} +\end{example} + + +\rSec1[ub.cpp]{\ref{cpp}: Preprocessing directives} + +\rSec2[ub.cpp.cond]{Conditional inclusion} + +\pnum +\ubxref{cpp.cond.defined} \\ +If the token defined is generated as a result of this replacement process or use of +the defined unary operator does not match one of the two specified forms prior to macro replacement, the +behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#define A +#define B defined(A) +#if B // undefined behavior, \tcode{defined} is generated by macro replacement for controlling expression +#endif +\end{codeblock} +\end{example} + + +\rSec2[ub.cpp.include]{Source file inclusion} + +\pnum +\ubxref{cpp.include.one.of.two.forms} \\ +If the an include directive after replacement does not match one of the two forms +outlined [cpp.indlude]\iref{cpp.include} then the behavior is undefined + +\pnum +\begin{example} +\begin{codeblock} +#include " // undefined behavior, does not match one of the allowable forms +\end{codeblock} +\end{example} + + +\rSec2[ub.cpp.replace]{Macro replacement} + +\pnum +\ubxref{cpp.replace.macro.pptoken} \\ +If there are sequences of preprocessing tokens within the list of arguments to a function-like macro +that would otherwise act as preprocessing directives, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#define M(d, n) d = n + +void f(int x) { + M(x, +#ifdef D // undefined behavior, preprocessing directive + 16 +#else // undefined behavior, preprocessing directive + 32 +#endif // undefined behavior, preprocessing directive + ); +} + +int main() { + int x; + f(x); +} +\end{codeblock} +\end{example} + + +\rSec2[ub.cpp.stringize]{The \# operator} + +\pnum +\ubxref{cpp.stringize.invalid.char} \\ +If an invalid character results from using the stringize operator the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#define s_lit(x) #x +s_lit( \ ) // stringizes to "\tcode{\textbackslash}"", which is not a valid token +\end{codeblock} +\end{example} + + +\rSec2[ub.cpp.concat]{The \#\# operator} + +\pnum +\ubxref{cpp.concat.invalid.preprocessing.token} \\ +If an invalid preprocessing token results from using the concat operator the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#define cat(x, y) x##y + +void f() { + cat(/, /) // Undefined behavior // is not a valid preprocessing token +} +\end{codeblock} +\end{example} + + +\rSec2[ub.cpp.line]{Line control} + +\pnum +\ubxref{cpp.line.zero.or.overflow} \\ +When using the \tcode{\#line} directive, if the digit sequence +specifies zero or a number greater than 2147483647, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#line 4294967295 // undefined behavior, number greater than 2147483647 + +int main() { return notdefined; } +\end{codeblock} +\end{example} + +\pnum +\ubxref{cpp.line.pptoken.not.match} \\ +When using the \grammarterm{pp-tokens} variant of the \tcode{\#line} directive, if the directive resulting after all replacements does not match +one of the two previous forms, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#define cat(a, b) a##b +#line cat(10, e20) +\end{codeblock} +\end{example} + +\rSec2[ub.cpp.predefined]{Predefined macro names} + +\pnum +\ubxref{cpp.predefined.define.undef} \\ +If any of the pre-defined macro names in [cpp.predefined], or the identifier defined, is the subject of a \tcode{\#define} +or a \tcode{\#undef} preprocessing directive, the behavior is undefined. + +\pnum +\begin{example} +\begin{codeblock} +#define __cplusplus 300012L +\end{codeblock} +\end{example} diff --git a/tools/check-output.sh b/tools/check-output.sh index ccae6b348b..39546eb0ec 100755 --- a/tools/check-output.sh +++ b/tools/check-output.sh @@ -36,7 +36,7 @@ rm -f tmp.txt # Find bad labels grep newlabel `ls *.aux | grep -v std.aux` | awk -F '{' '{ print $2 }' | - sed 's/}//g' | sed 's/^tab://;s/fig://;s/eq://;s/idx.*\..//' | + sed 's/}//g' | sed 's/^tab://;s/fig://;s/eq://;s/ub://;s/ubx://;s/ifndr://;s/ifndrx://;s/idx.*\..//' | grep -v '^[a-z.0-9]*$' | sed 's/^\(.*\)$/bad label \1/' | fail || failed=1 diff --git a/tools/check-source.sh b/tools/check-source.sh index 42596cfbb4..514f81896c 100755 --- a/tools/check-source.sh +++ b/tools/check-source.sh @@ -227,7 +227,7 @@ done | fail 'hanging paragraph' || failed=1 # Subclauses without siblings -for f in $texfiles; do +for f in `ls $texfiles | grep -v ub.tex | grep -v ifndr.tex`; do sed -n '/^\\rSec/{=;p;}' $f | # prefix output with filename and line sed '/^[0-9]\+$/{N;s/\n/:/;}' | sed "s/.*/$f:&/" |