diff --git a/source/basic.tex b/source/basic.tex index bf53cb3f67..59d6235a80 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -674,7 +674,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\ifndrdef{basic.def.odr.exact.one.def}. +outside of a discarded statement\iref{stmt.if}; no diagnostic required\ifndrdef{basic.def.odr.minimum.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}, @@ -762,21 +762,23 @@ that definition shall be an injected declaration having the same characteristic sequence as $X$; a diagnostic is required only if \tcode{D} is attached to a named module and -a prior definition is reachable at the point where a later definition occurs. +a prior definition is reachable +at the point where a later definition occurs\ifndrdef{basic.def.odr.injected.match}. \pnum For any other definable item \tcode{D} with definitions in multiple translation units, \begin{itemize} \item -if \tcode{D} is a non-inline non-templated function or variable, or +if \tcode{D} is a non-inline non-templated function or variable\ifndrdef{basic.def.odr.maximum.one.def}, or \item if the definitions in different translation units -do not satisfy the following requirements, +do not satisfy the following requirements\ifndrdef{basic.def.odr.definition.matches}, \end{itemize} the program is ill-formed; a diagnostic is required only if the definable item is attached to a named module and -a prior definition is reachable at the point where a later definition occurs. +a prior definition is reachable +at the point where a later definition occurs. Given such an item, for all definitions of \tcode{D}, or, if \tcode{D} is an unnamed enumeration, @@ -3166,7 +3168,8 @@ declarations for an array object can specify array types that differ by the presence or absence of a major array bound\iref{dcl.array}. -No diagnostic is required if neither declaration is reachable from the other. +No diagnostic is required +if neither declaration is reachable from the other\ifndrdef{basic.link.consistent.types}. \begin{example} \begin{codeblock} int f(int x, int x); // error: different entities for \tcode{x} @@ -3840,7 +3843,7 @@ using the \grammarterm{alignment-specifier}\iref{dcl.align}. Attempting to create an object\iref{intro.object} in storage that does not meet the alignment requirements of the object's type -is undefined behavior.\ubdef{basic.align.object.alignment} +is undefined behavior\ubdef{basic.align.object.alignment}. \pnum A \defnadj{fundamental}{alignment} is represented by an alignment @@ -4585,7 +4588,7 @@ \tcode{p0} represents the address of a block of storage disjoint from the storage for any other object accessible to the caller. The effect of indirecting through a pointer -returned from a request for zero size is undefined.\ubdef{basic.stc.alloc.zero.dereference} +returned from a request for zero size is undefined\ubdef{basic.stc.alloc.zero.dereference}. \begin{footnote} The intent is to have \tcode{\keyword{operator} \keyword{new}()} implementable by @@ -4708,7 +4711,7 @@ signature. \pnum -If a deallocation function terminates by throwing an exception, the behavior is undefined.\ubdef{basic.stc.alloc.dealloc.throw} +If a deallocation function terminates by throwing an exception, the behavior is undefined\ubdef{basic.stc.alloc.dealloc.throw}. The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect. @@ -7794,7 +7797,7 @@ An invocation of the macro \tcode{va_start}\iref{cstdarg.syn} shall not be a subexpression of the predicate of a contract assertion, -no diagnostic required. +no diagnostic required\ifndrdef{basic.contract.vastart.contract.predicate}. \pnum \begin{note} @@ -8200,6 +8203,6 @@ If the contract-violation handler is not replaceable, a declaration of a replacement function for the contract-violation handler -is ill-formed, no diagnostic required. +is ill-formed, no diagnostic required.\ifndrdef{basic.contract.handler.replacing.nonreplaceable} \indextext{contract assertion|)} diff --git a/source/declarations.tex b/source/declarations.tex index ae650118b0..88830bfea2 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -1065,7 +1065,7 @@ If the specifier is applied to any declaration of a variable, it shall be applied to the initializing declaration. No diagnostic is required if no \keyword{constinit} declaration -is reachable at the point of the initializing declaration. +is reachable at the point of the initializing declaration\ifndrdef{dcl.constinit.specifier.not.reachable}. \pnum If a variable declared with the \keyword{constinit} specifier has @@ -1140,7 +1140,7 @@ is declared inline in one definition domain, an inline declaration of it shall be reachable from the end of every definition domain in which it is declared; -no diagnostic is required. +no diagnostic is required\ifndrdef{dcl.inline.missing.on.definition}. \begin{note} A call to an inline function or a use of an inline variable can be encountered before its definition becomes reachable in a translation unit. @@ -3286,7 +3286,7 @@ the converted initializer is a glvalue whose type is not call-compatible\iref{expr.call} with the type of the function's definition -results in undefined behavior.\ubdef{dcl.ref.incompatible.function} +results in undefined behavior\ubdef{dcl.ref.incompatible.function}. Attempting to bind a reference to an object where the converted initializer is a glvalue through which the object is not type-accessible\iref{basic.lval} @@ -3304,7 +3304,7 @@ \end{note} The behavior of an evaluation of a reference\iref{expr.prim.id, expr.ref} that does not happen after\iref{intro.races} the initialization of the reference -is undefined.\ubdef{dcl.ref.uninitialized.reference} +is undefined\ubdef{dcl.ref.uninitialized.reference}. \begin{example} \begin{codeblock} int &f(int&); @@ -4376,7 +4376,8 @@ \end{example} For a given inline function defined in different translation units, the accumulated sets of default arguments at the end of the -translation units shall be the same; no diagnostic is required. +translation units shall be the same; +no diagnostic is required\ifndrdef{dcl.fct.default.inline.same.defaults}. If a friend declaration $D$ specifies a default argument expression, that declaration shall be a definition and there shall be no other declaration of the function or function template @@ -4681,7 +4682,8 @@ a declaration $F_2$ is a first declaration of \tcode{f} in another translation unit, $F_1$ and $F_2$ shall specify the same -\grammarterm{function-contract-specifier-seq}, no diagnostic required. +\grammarterm{function-contract-specifier-seq}, +no diagnostic required\ifndrdef{dcl.contract.func.mismatched.contract.specifiers}. \pnum A \grammarterm{function-contract-specifier-seq} $S_1$ @@ -7590,7 +7592,7 @@ shall be such that it would be valid as a redeclaration of the declaration in that header; \end{itemize} -no diagnostic is required. +no diagnostic is required\ifndrdef{dcl.fct.def.replace.bad.replacement}. \begin{note} The one-definition rule\iref{basic.def.odr} applies to the definitions of a replaceable function @@ -9311,7 +9313,7 @@ \pnum If two declarations of an entity give it different language linkages, the program is ill-formed; no diagnostic is required if neither declaration -is reachable from the other. +is reachable from the other\ifndrdef{dcl.link.mismatched.language.linkage}. \indextext{consistency!linkage specification}% A redeclaration of an entity without a linkage specification inherits the language linkage of the entity and (if applicable) its type. @@ -9899,7 +9901,7 @@ in one translation unit and the same function is declared without the \tcode{indeterminate} attribute on the same parameter in its first declaration in another translation unit, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{dcl.attr.indet.mismatched.declarations}. \pnum \begin{note} diff --git a/source/expressions.tex b/source/expressions.tex index 90b9336eed..29b347b9cf 100644 --- a/source/expressions.tex +++ b/source/expressions.tex @@ -326,7 +326,7 @@ a defaulted copy/move constructor or copy/move assignment operator for a union of type \tcode{U} with a glvalue argument that does not denote an object of type \cv{}~\tcode{U} within its lifetime, -the behavior is undefined.\ubdef{expr.basic.lvalue.union.initialization} +the behavior is undefined\ubdef{expr.basic.lvalue.union.initialization}. \begin{note} In C, an entire object of structure type can be accessed, e.g., using assignment. By contrast, \Cpp{} has no notion of accessing an object of class type @@ -345,7 +345,7 @@ If a pointer to $X$ would be valid in the context of the evaluation of the expression\iref{basic.fundamental}, the result designates $X$; -otherwise, the behavior is undefined.\ubdef{expr.type.reference.lifetime} +otherwise, the behavior is undefined\ubdef{expr.type.reference.lifetime}. \begin{note} Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see~\ref{basic.life}). @@ -686,7 +686,7 @@ \item Otherwise, if the bits in the value representation of the object to which the glvalue refers -are not valid for the object's type, the behavior is undefined.\ubdef{conv.lval.valid.representation} +are not valid for the object's type, the behavior is undefined\ubdef{conv.lval.valid.representation}. \begin{example} \begin{codeblock} bool f() { @@ -1024,8 +1024,8 @@ exactly as a value of the floating-point type. \end{note} If the value being converted is -outside the range of values that can be represented, the behavior is undefined. -\ubdef{conv.fpint.int.not.represented} +outside the range of values that can be represented, +the behavior is undefined\ubdef{conv.fpint.int.not.represented}. If the source type is \keyword{bool}, the value \keyword{false} is converted to zero and the value \keyword{true} is converted to one. @@ -1079,7 +1079,7 @@ that is within its lifetime or within its period of construction or destruction\iref{class.cdtor}, -the behavior is undefined.\ubdef{conv.ptr.virtual.base} +the behavior is undefined\ubdef{conv.ptr.virtual.base}. Otherwise, the result is a pointer to the base class subobject of the derived class object. @@ -1113,7 +1113,7 @@ \tcode{D}, a program that necessitates this conversion is ill-formed. If class \tcode{D} does not contain the original member and is not a base class of the class containing the original member, -the behavior is undefined.\ubdef{conv.member.missing.member} +the behavior is undefined\ubdef{conv.member.missing.member}. Otherwise, the result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class @@ -4523,14 +4523,14 @@ that is within its lifetime or within its period of construction or destruction\iref{class.cdtor}, -the behavior is undefined.\ubdef{expr.dynamic.cast.pointer.lifetime} +the behavior is undefined\ubdef{expr.dynamic.cast.pointer.lifetime}. If \tcode{v} is a glvalue of type \tcode{U} and \tcode{v} does not refer to an object whose type is similar to \tcode{U} and that is within its lifetime or within its period of construction or destruction, -the behavior is undefined.\ubdef{expr.dynamic.cast.glvalue.lifetime} +the behavior is undefined\ubdef{expr.dynamic.cast.glvalue.lifetime}. \pnum If \tcode{T} is ``pointer to \cv{} \keyword{void}'', then the result @@ -4894,7 +4894,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\ubdef{expr.static.cast.does.not.contain.orignal.member}. +to the original member. Otherwise, the behavior is undefined\ubdef{expr.static.cast.does.not.contain.original.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 diff --git a/source/ifndr.tex b/source/ifndr.tex index 0185e2ccfa..d1b8c44137 100644 --- a/source/ifndr.tex +++ b/source/ifndr.tex @@ -4,19 +4,21 @@ \rSec1[ifndr.general]{General} 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. +the following phrases: +``no diagnostic is required'', +``no diagnostic required'', or +``a diagnostic is required only if''. 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} +\ifndrdescription{lex.name.reserved} \pnum -\ifndrxref{lex.name.reserved} -Some identifiers are reserved for use by \Cpp{} implementations and shall not be used otherwise; no -diagnostic is required. +Using an identifier reserved for use by \Cpp{} +is ill-formed, no diagnostic required. \pnum \begin{example} @@ -31,15 +33,42 @@ \end{codeblock} \end{example} - \rSec1[ifndr.basic]{\ref{basic}: Basics} -\rSec2[ifndr.basic.def.odr]{One-definition rule} +\ifndrdescription{basic.link.consistent.types} \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. +Having multiple declarations of the same entity +with different kinds +when those declarations are not reachable from one another +is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Module interface of \tcode{M}} +void g(); // \#1 +void h(); // \#2 +template int j; // \#3 +\end{codeblocktu} +\begin{codeblocktu}{Module interface of \tcode{N}} +int g(); // same entity as \#1, different type +namespace h {} // same entity as \#2, not both namespaces +template int j; // same entity as \#3, non-equivalent template heads +\end{codeblocktu} +\begin{codeblocktu}{Other translation unit} +import M; +import N; + // ill-formed, no diagnostic required due to the mismatched pairs above +\end{codeblocktu} +\end{example} + +\ifndrdescription{basic.def.odr.minimum.one.def} + +\pnum +Not having a definition +for a function or variable +that is odr-used from a non-discarded statement\iref{stmt.if} +is ill-formed, no diagnostic required. \pnum \begin{example} @@ -55,13 +84,98 @@ \end{codeblock} \end{example} +\ifndrdescription{basic.def.odr.injected.match} + +\pnum +Defining a definable item \tcode{D} +with an injected declaration\iref{expr.const.reflect} +in one translation unit and +a definition in a different translation unit +when \tcode{D} is not attached to a named module +or neither definition is reachable from the other +is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +struct S; +consteval { std::meta::define_aggregate(^^S, {}); } // \#1 +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +struct S {}; // IFNDR, definition here, injected declaration at \#1 +\end{codeblocktu} +\end{example} + +\ifndrdescription{basic.def.odr.maximum.one.def} + +\pnum +If there are definitions +in different translation units +of a non-inline non-templated function or variable +that are not attached to a named module +or are not reachable from one another, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +export module M:A; // module partition +void f() {} // \#1 +void g() {} // \#2 +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +export module M:B; // module partition +void f() {} // IFNDR, \#1 not reachable +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#3} +export module M; // primary module interface unit +export import :A; +void g(); // error: \#2 is reachable +\end{codeblocktu} +\end{example} + +\ifndrdescription{basic.def.odr.definition.matches} \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. +If there are definitions +in different translation units +of a definable item \tcode{D} +where +\begin{itemize} +\item $D$ is not defined by an injected declaration\iref{expr.const.reflect}, +\item $D$ is not an inline or templated function or variable, and +\item $D$ is not attached to a named module or the declarations are not reachable from one another, +\end{itemize} +that do not satisfy the matching rules described in \ref{basic.def.odr}, +the program is ill-formed, no diagnostic required. +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +inline void f() {} // \#1 +inline void g() {} // \#2 +inline void h() {[]{}();} // \#3 +namespace { int i = 0; } +inline void j() {++i;} // \#4 +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +inline void f() {} // OK, same as \#1 +inline void g() {;} // IFNDR, different sequence of tokens than \#2 +inline void h() {[]{}();} // IFNDR, closure has different type than \#3 +namespace { int i = 0; } +inline void j() {++i; } // IFNDR, \tcode{i} refers to different entity than in \#4 +\end{codeblocktu} +\end{example} + +\ifndrdescription{basic.def.odr.unnamed.enum.same.type} + +\pnum +Having multiple unnamed enumeration definitions +in the same scope +that have the same first enumerator name +and do not have typedef names for linkage purposes\iref{dcl.enum} +that are not the same enumeration +is ill-formed, no diagnostic required. \pnum \begin{example} @@ -75,17 +189,55 @@ // main.cpp import "a.h"; import "b.h"; -auto n = decltype(a)::b; // ill-formed no diagnostic required, more than one unnammed enum +auto n = decltype(a)::b; // ill-formed no diagnostic required, more than one unnamed enum // definition reachable at this point but their types are not the same \end{codeblock} \end{example} +\ifndrdescription{basic.contract.vastart.contract.predicate} + +\pnum +The use of \tcode{va_start}\iref{cstdarg.syn} +within the predicate of a contract assertion +is ill-formed, no diagnostic required; + +\pnum +\begin{example} +\begin{codeblock} +void f(...) +{ + va_list args; + contract_assert((va_start(const_cast(args)), true)) // ill-formed, no diagnostic required +} +\end{codeblock} +\end{example} + +\ifndrdescription{basic.contract.handler.replacing.nonreplaceable} + +\pnum +On platforms where +the contract-violation handler +is not replaceable\iref{term.replaceable.function} +a function definition which could be such a replacement function +is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +#include +void handle_contract_violation(const std::contract_violation& violation) {} + // ill-formed, no diagnostic required if violation-handler is not replaceable +\end{codeblock} +\end{example} -\rSec2[ifndr.class.member.lookup]{Member name lookup} +\ifndrdescription{class.member.lookup.name.refers.diff.decl} \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. +A name $N$ used in a class $S$ +referring to a different declaration +when resolved in its context +than when re-evaluated in the completed scope of $S$ +is ill-formed, no diagnostic required. \pnum \begin{example} @@ -125,13 +277,11 @@ \end{codeblock} \end{example} - \rSec1[ifndr.expr]{\ref{expr}: Expressions} -\rSec2[ifndr.expr.prim.req]{Requires expressions} +\ifndrdescription{expr.prim.req.always.sub.fail} \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. @@ -145,13 +295,11 @@ \end{codeblock} \end{example} +\rSec1[ifndr.stmt]{\ref{stmt}: Statements} -\rSec1[ifndr.stmt.stmt]{\ref{stmt}: Statements} - -\rSec2[ifndr.stmt.ambig]{Ambiguity resolution} +\ifndrdescription{stmt.ambig.bound.diff.parse} \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. @@ -175,13 +323,136 @@ \end{codeblock} \end{example} +\rSec1[ifndr.dcl]{\ref{dcl}: Declarations} + +\ifndrdescription{dcl.constinit.specifier.not.reachable} + +\pnum +If the initializing declaration +of a variable without the \tcode{constinit} specifier +has the \tcode{constinit} specifier +applied to declarations that are not reachable from +that initializing declaration, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +int x = 5; // initializing declaration of \tcode{x} +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +extern constinit int x; // IFNDR, not reachable from initializing declaration of \tcode{x} +\end{codeblocktu} +\end{example} + +\ifndrdescription{dcl.inline.missing.on.definition} + +\pnum +If a function or variable +with external or module linkage +is declared inline +but there is no inline declaration +reachable from the end of some definition domain +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +inline int f(); +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +int f() { return 17; } + // IFNDR, end of definition domain but no inline declaration of \tcode{f} is reachable. +\end{codeblocktu} +\end{example} + +\ifndrdescription{dcl.fct.default.inline.same.defaults} + +\pnum +If the accumulated set of default arguments +for a given inline function +with definitions in multiple translation units +is different at the end +of different translation units, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +inline int f(int x, int y = 2); +inline int f(int x = 1, int y); + // IFNDR, default arguments of \tcode{f} are \tcode{1} and \tcode{2} +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +inline int f(int x = 3, int y = 4); + // IFNDR, default arguments of \tcode{f} are \tcode{3} and \tcode{4} +\end{codeblocktu} +\end{example} + +\ifndrdescription{dcl.contract.func.mismatched.contract.specifiers} + +\pnum +If two different first declarations of a function +(which must therefore not be reachable from one another) +do not have equivalent function contract specifiers +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +int f(int x) pre(x >= 0); // IFNDR, different function contract specifiers from the other first declaration of \tcode{f} +int g(int x) pre(x == 0); // IFNDR, different function contract specifiers from the other first declaration of \tcode{g} +int h(int x) pre(x <= 0); // OK, equivalent function contract specifiers to the other first declaration of \tcode{h} +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +int f(int x); // IFNDR, different function contract specifiers from the other first declaration of \tcode{f} +int g(int x) pre(x != 0); // IFNDR, different function contract specifiers from the other first declaration of \tcode{g} +int h(int y) pre(y <= 0); // OK, equivalent function contract specifiers to the other first declaration of \tcode{h} +\end{codeblocktu} +\end{example} + +\ifndrdescription{dcl.fct.def.replace.bad.replacement} -\rSec1[ifndr.dcl.dcl]{\ref{dcl}: Declarations} +\pnum +A declaration of a replaceable function +that is inline, +not attached to the global module, +does not have \Cpp{} language linkage, +does not have the required return type, +or is not a valid redeclaration of the +corresponding declaration in a standard library header (if there is one) +then the program is ill-formed, no diagnostic required. -\rSec2[ifndr.dcl.align]{Alignment specifier} +\pnum +\begin{example} +\begin{codeblock} +extern "C" // IFNDR, wrong language linkage +inline // IFNDR, inline +int // IFNDR, wrong return type +handle_contract_violation(const std::contract::contract_violation&) {} + +void* operator new(decltype(sizeof(0))) noexcept; // IFNDR, mismatched exception specification to declaration + // in \tcode{} +\end{codeblock} +\end{example} + +\ifndrdescription{dcl.link.mismatched.language.linkage} + +\pnum +If two declarations of an entity +do not have the same language linkage +and neither is reachable from the other +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +\end{codeblock} +\end{example} + +\ifndrdescription{dcl.align.diff.translation.units} \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. @@ -196,28 +467,53 @@ \end{codeblocktu} \end{example} -\rSec2[ifndr.dcl.attr.noreturn]{Noreturn attribute} +\ifndrdescription{dcl.attr.indet.mismatched.declarations} + +\pnum +If two first declarations of a function +declare a function parameter with +mismatched uses of the \tcode{indeterminate} attribute, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +int h(int x [[indeterminate]]); // IFNDR, mismatched \tcode{[[indeterminate]]} to other first declaration of \tcode{h} +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +int h(int x); // IFNDR, mismatched \tcode{[[indeterminate]]} to other first declaration of \tcode{h} +\end{codeblocktu} +\end{example} + +\ifndrdescription{dcl.attr.noreturn.trans.unit.mismatch} + +\pnum +No diagnostic is required if a function is declared +in one translation unit with the \tcode{noreturn} attribute +but has declarations in other translation units +without the attribute. \pnum -\ifndrxref{dcl.attr.noreturn.trans.unit.mismatch} \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} +void f(); // 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} +\ifndrdescription{module.unit.reserved.identifiers} \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. +Specifying a \grammarterm{module-name} +beginning with an identifier +consisting of \tcode{std} followed by zero or more digits, +or containing a reserved identifier\iref{lex.token} +in a \grammarterm{module-declaration} +is ill-formed, no diagnostic required. \pnum \begin{example} @@ -230,12 +526,12 @@ \end{codeblock} \end{example} +\ifndrdescription{module.unit.named.module.no.partition} \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. +Having multiple primary module interface units +for a named module +is ill-formed, no diagnostic required. \pnum \begin{example} @@ -245,13 +541,56 @@ \end{codeblock} \end{example} +\ifndrdescription{module.unit.unexported.module.partition} + +\pnum +If a module partition of a module +that is a module interface unit +but is not directly or indirectly exported +by the primary module interface unit\iref{module.import}, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +export module M; // primary module interface unit +export import :A; +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +export module M:A; // OK, directly exported by \tcode{M} +export import :B; +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#3} +export module M:B; // OK, indirectly exported by \tcode{M} +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#4} +export module M:C; // IFNDR, not directly or indirectly exported by \tcode{M} +\end{codeblocktu} +\end{example} + +\ifndrdescription{module.private.frag.other.module.units} + +\pnum +If a module has a private module fragment +and there is another module unit of that module, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{Translation unit \#1} +export module M; +module :private; // private module fragment +\end{codeblocktu} +\begin{codeblocktu}{Translation unit \#2} +module M:A; // IFNDR, partition of \tcode{M} with private module fragment +\end{codeblocktu} +\end{example} \rSec1[ifndr.class]{\ref{class}: Classes} -\rSec2[ifndr.class.base.init]{Initializing bases and members} +\ifndrdescription{class.base.init.delegate.itself} \pnum -\ifndrxref{class.base.init.delegate.itself} If a constructor delegates to itself directly or indirectly, the program is ill-formed, no diagnostic required @@ -267,13 +606,12 @@ \end{codeblock} \end{example} - -\rSec2[ifndr.class.virtual]{Virtual functions} +\ifndrdescription{class.virtual.pure.or.defined} \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 +If a virtual function that is not pure +has no definition, +the program is ill-formed, no diagnostic required. \pnum \begin{example} @@ -283,18 +621,16 @@ }; int main() { - A a; // ill-formed no diagnostic required, virtual function that is not pure but has not definition + A a; // ill-formed no diagnostic required, virtual function that is not pure but has no definition } \end{codeblock} \end{example} - \rSec1[ifndr.over]{\ref{over}: Overloading} -\rSec2[ifndr.over.literal]{User-defined literals} +\ifndrdescription{over.literal.reserved} \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. @@ -309,14 +645,19 @@ \rSec1[ifndr.temp]{\ref{temp}: Templates} -\rSec2[ifndr.temp.pre]{Preamble} +\ifndrdescription{temp.pre.reach.def} \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. +A definition of a function template, +member function of a class template, +variable template, +or static data member of a class template +that is not reachable from +the end of every definition domain\iref{basic.def.odr} +in which it is implicitly instantiated\iref{temp.inst} +and whose corresponding specialization +is not explicitly instantiated\iref{temp.explicit} in some translation unit +is ill-formed, no diagnostic required. \pnum \begin{example} @@ -328,21 +669,22 @@ // a.cpp #include "a.h" int main() { - f(); // ill-formed no diagnostic required, function template implicity + f(); // ill-formed no diagnostic required, function template implicitly // instantiated but not reachable definition } \end{codeblock} \end{example} - -\rSec2[ifndr.temp.arg.template]{Template template arguments} +\ifndrdescription{temp.arg.template.sat.constraints} \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. +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} @@ -366,14 +708,13 @@ \end{codeblock} \end{example} - -\rSec2[ifndr.constr.atomic]{Atomic constraints} +\ifndrdescription{temp.constr.atomic.equiv.but.not.equiv} \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. +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} @@ -389,9 +730,13 @@ \end{codeblock} \end{example} +\ifndrdescription{temp.constr.atomic.sat.result.diff} + \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. +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} @@ -407,12 +752,11 @@ \end{codeblock} \end{example} - -\rSec2[ifndr.temp.constr.normal]{Constraint normalization} +\ifndrdescription{temp.constr.normal.invalid} \pnum -\ifndrxref{temp.constr.normal.invalid} -If during constraint normalization any such substitution results in an invalid type or expression, +If during constraint normalization +any such substitution results in an invalid type or expression, the program is ill-formed; no diagnostic is required \pnum @@ -425,13 +769,14 @@ \end{codeblock} \end{example} - -\rSec2[ifndr.temp.spec.partial]{Partial specialization} +\ifndrdescription{temp.spec.partial.general.partial.reachable} \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. +If a partial specialization +is not reachable from a use of a template specialization +that would make use of that partial specialization +as the result of an implicit or explicit instantiation, +the program is ill-formed, no diagnostic required. \pnum \begin{example} @@ -451,37 +796,13 @@ \end{codeblock} \end{example} - -\rSec2[ifndr.temp.names]{Names of template specializations} +\ifndrdescription{temp.over.link.equiv.not.equiv} \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. +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} @@ -495,16 +816,16 @@ \end{codeblock} \end{example} -\rSec2[ifndr.temp.res]{Name resolution} - -\rSec3[ifndr.temp.res.general]{General} +\ifndrdescription{temp.res.general.default.but.not.found} \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. +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} @@ -512,16 +833,19 @@ \end{codeblock} \end{example} -\rSec2[ifndr.temp.dep.res]{Dependent name resolution} +%WORDINGTODO: SY, JMB: We need to produce an example for this case. -\rSec3[ifndr.temp.point]{Point of instantiation} +\ifndrdescription{temp.point.diff.pt.diff.meaning} \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. +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\iref{basic.def.odr}, +the program is ill-formed, no diagnostic required. \pnum \begin{example} @@ -547,15 +871,36 @@ \end{codeblock} \end{example} +\ifndrdescription{temp.dep.candidate.different.lookup.different} -\rSec2[ifndr.temp.explicit]{Explicit instantiation} +\pnum +If considering all function declarations +with external linkage +in the associated namespaces in all translations +would make a dependent call\iref{temp.dep} ill-formed +or find a better match, +the program is ill-formed, no diagnostic required. \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. +\begin{example} +\begin{codeblock} +\end{codeblock} +\end{example} + +%WORDINGTODO: JMB: produce an example + +\ifndrdescription{temp.explicit.decl.implicit.inst} + +\pnum +If 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 +is not the subject of +an explicit instantiation definition somewhere in the program +the program is ill-formed, no diagnostic required. \pnum \begin{example} @@ -570,26 +915,117 @@ \end{codeblock} \end{example} +\ifndrdescription{temp.expl.spec.unreachable.declaration} + +\pnum +If an implicit instantiation of a template would occur +and there is an unreachable explicit specialization +that would have matched, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +\end{codeblock} +\end{example} + +%WORDINGTODO: JMB: produce an example + +\ifndrdescription{temp.expl.spec.missing.definition} -\rSec2[ifndr.temp.deduct]{Template argument deduction} +\pnum +If an explicit specialization of a template is +declared but there is no definition provided +for that specialization, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +template int f(T&&) { return 0; } +template <> int f(int&&); +int j = f(1); // IFNDR, odr-use of \tcode{f} with no definition +\end{codeblock} +\end{example} -\rSec3[ifndr.temp.deduct.general]{General} +\ifndrdescription{temp.deduct.general.diff.order} \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. +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 struct A { using X = typename T::X; }; +template typename T::X h(typename A::X); // \#1 +template auto h(typename A::X) -> typename T::X; // redeclaration \#2 template void h(...) { } void x() { h(0); // ill-formed, no diagnostic required + // \#1 fails to find \tcode{T::X} and instantiates nothing + // \#2 instantiates \tcode{A} } \end{codeblock} \end{example} + +%WORDINGTODO: JMB: Someone should confirm that the comments correctly describe why +%this example is ill-formed. + +\rSec1[ifndr.cpp]{\ref{cpp}: Preprocessing directives} + +\ifndrdescription{cpp.cond.defined.after.macro} + +\pnum +If the expansion of a macro produces the preprocessing token \tcode{defined} +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +#define A defined +#if A // Ill-formed no diagnostic required, \tcode{defined} is generated by macro replacement + // in controlling expression +#endif +\end{codeblock} +\end{example} + +\ifndrdescription{cpp.cond.defined.malformed} + +\pnum +If the \tcode{defined} unary operator is used when it +does not match +one of the specified grammatical forms, +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +#define A +#define B A) +#if defined ( B // Ill-formed no diagnostic required, unary operator \tcode{defined} did not match + // valid form before replacement +#endif +\end{codeblock} +\end{example} + +\ifndrdescription{cpp.include.malformed.headername} + +\pnum +If the \grammarterm{header-name-tokens} after +an \tcode{include} directive +cannot be formed into a \grammarterm{header-name} +(with implementation-defined treatment of whitespace), +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +#include `` // Ill-formed no diagnostic required, does not match one of the two allowable forms +\end{codeblock} +\end{example} diff --git a/source/macros.tex b/source/macros.tex index dad5db884e..3b69c5bf6d 100644 --- a/source/macros.tex +++ b/source/macros.tex @@ -386,17 +386,6 @@ \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}} @@ -812,3 +801,45 @@ } \newcommand{\defncontext}[1]{\textlangle#1\textrangle} \newnoteenvironment{defnote}{Note \arabic{defnote} to entry}{end note} + +%%---------------------------------- +%% UB and IFNDR sections for annexes +%%---------------------------------- +%% 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 for sections describing each individual IFNDR +\newcommand{\ubdescription}[1]{% +\let\oldcontentsline\addcontentsline% +\let\addcontentsline\nocontentsline% +\ifcase\value{SectionDepth} + \let\s=\section + \or\let\s=\subsection + \or\let\s=\subsubsection + \or\let\s=\paragraph + \or\let\s=\subparagraph + \fi% +\s[#1]{\hfill[#1]}\vspace{-.3\onelineskip}\label{ubx:#1} % +\textbf{Specified in:}\space\ref{ub:#1}% +\let\addcontentsline\oldcontentsline% +\vspace{-\parskip}% +} + +% Use in main body of text to mark IFNDR +\newcommand{\ifndrdef}[1]{\nolinebreak[3] (\label{ifndr:#1}\ref{ifndrx:#1})} +\newcommand{\ifndrxref}[1]{% +\let\oldcontentsline\addcontentsline% +\let\addcontentsline\nocontentsline% +\ifcase\value{SectionDepth} + \let\s=\section + \or\let\s=\subsection + \or\let\s=\subsubsection + \or\let\s=\paragraph + \or\let\s=\subparagraph + \fi% +\s[#1]{\hfill[#1]}\vspace{-.3\onelineskip}\label{ifndrx:#1} % +\textbf{Specified in:}\space\ref{ifndr:#1}% +\let\addcontentsline\oldcontentsline% +\vspace{-\parskip}% +} diff --git a/source/modules.tex b/source/modules.tex index fea1cc14e3..9ecef29d77 100644 --- a/source/modules.tex +++ b/source/modules.tex @@ -66,7 +66,8 @@ that are module interface units shall be directly or indirectly exported by the primary module interface unit\iref{module.import}. -No diagnostic is required for a violation of these rules. +No diagnostic is required +for a violation of these rules\ifndrdef{module.unit.unexported.module.partition}. \begin{note} Module partitions can be imported only by other module units in the same module. @@ -810,7 +811,7 @@ in a primary module interface unit\iref{module.unit}. A module unit with a \grammarterm{private-module-fragment} shall be the only module unit of its module; -no diagnostic is required. +no diagnostic is required\ifndrdef{module.private.frag.other.module.units}. \pnum \begin{note} diff --git a/source/preprocessor.tex b/source/preprocessor.tex index 0bfb7f194f..c05a798b76 100644 --- a/source/preprocessor.tex +++ b/source/preprocessor.tex @@ -527,12 +527,12 @@ as a macro\iref{cpp.replace.general}, the program is ill-formed. If the preprocessing token \tcode{defined} -is generated as a result of this replacement process +is generated as a result of this replacement process\ifndrdef{cpp.cond.defined.after.macro} or use of the \tcode{defined} unary operator does not match one of the two specified forms prior to macro replacement, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{cpp.cond.defined.malformed}. \pnum After all replacements due to macro expansion and @@ -757,7 +757,8 @@ is \impldef{treatment of whitespace when processing a \tcode{\#include} directive}. If the attempt succeeds, the directive with the so-formed \grammarterm{header-name} is processed as specified for the previous form. -Otherwise, the program is ill-formed, no diagnostic required. +Otherwise, the program is +ill-formed, no diagnostic required\ifndrdef{cpp.include.malformed.headername}. \begin{note} Adjacent \grammarterm{string-literal}s are not concatenated into a single \grammarterm{string-literal} diff --git a/source/templates.tex b/source/templates.tex index 8ce89369d1..de6fd2c460 100644 --- a/source/templates.tex +++ b/source/templates.tex @@ -987,7 +987,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}\ifndrdef{temp.names.sat.constraints}. +shall be satisfied\iref{temp.constr.constr}. \begin{example} \begin{codeblock} template concept C1 = sizeof(T) != sizeof(int); @@ -6281,7 +6281,7 @@ introduced in the associated namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts\iref{basic.lookup.argdep}, -then the program is ill-formed, no diagnostic required. +then the program is ill-formed, no diagnostic required\ifndrdef{temp.dep.candidate.different.lookup.different}. \pnum \begin{example} @@ -7168,7 +7168,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 requiredi\ifndrdef{temp.explicit.decl.implicit.inst}. +program; otherwise the program is ill-formed, no diagnostic required\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 @@ -7370,11 +7370,11 @@ every use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; -no diagnostic is required. +no diagnostic is required\ifndrdef{temp.expl.spec.unreachable.declaration}. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, -the program is ill-formed, no diagnostic required. +the program is ill-formed, no diagnostic required\ifndrdef{temp.expl.spec.missing.definition}. An implicit instantiation is never generated for an explicit specialization that is declared but not defined. \begin{example} diff --git a/source/ub.tex b/source/ub.tex index d913666ede..48caf464bc 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -13,10 +13,9 @@ \rSec1[ub.basic]{\ref{basic}: Basics} -\rSec2[ub.intro.object]{Implicitly creating object and undefined behavior} +\ubdescription{intro.object.implicit.create} \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 @@ -36,9 +35,9 @@ \end{codeblock} \end{example} +\ubdescription{intro.object.implicit.pointer} \pnum -\ubxref{intro.object.implicit.pointer} After implicitly creating objects within a specified region of storage, some operations are described as producing a pointer to a suitable created object\iref{basic.types}. @@ -79,7 +78,7 @@ int x; }; -*make_y() { +Y* make_y() { X* p1 = (X*)std::malloc(sizeof(struct X)); p1->a = 1; Y* p2 = (Y*)p1; @@ -90,10 +89,14 @@ \end{codeblock} \end{example} -\rSec2[ub.basic.align]{Object alignment} +%WORDINGTODO: SY: These comments feel too loose. +%Shouldn't we be saying something along the lines of p1 points to an +%object of type X whose lifetime was started but not ended .... p2 +%points to an object of type Y but its lifetime was not started ..." + +\ubdescription{basic.align.object.alignment} \pnum -\ubxref{basic.align.object.alignment} All instances of a type must be created in storage that meets the alignment requirement of that type. @@ -105,15 +108,16 @@ void make_misaligned() { alignas(S) char s[sizeof(S) + 1]; - new (&s+1) S(); // undefined behavior + new (&s+1) S(); // undefined behavior, \tcode{\&s+1} will yield a pointer to + // a \tcode{char} which is $1$ byte away from an address with + // an alignment of $4$ and so cannot have an alignment of $4$. } \end{codeblock} \end{example} -\rSec2[ub.basic.life]{Object lifetime} +\ubdescription{lifetime.outside.pointer.delete} \pnum -\ubxref{lifetime.outside.pointer.delete} For a pointer pointing to an object outside of its lifetime, behavior is undefined if 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}. @@ -135,8 +139,9 @@ \end{codeblock} \end{example} +\ubdescription{lifetime.outside.pointer.member} + \pnum -\ubxref{lifetime.outside.pointer.member} For a pointer pointing to an object outside of its lifetime, behavior is undefined if the pointer is used to access a non-static data member or call a non-static member function of the object. @@ -158,11 +163,12 @@ \end{codeblock} \end{example} +\ubdescription{lifetime.outside.pointer.virtual} + \pnum -\ubxref{lifetime.outside.pointer.virtual} For a pointer pointing to an object outside of its lifetime, behavior is -undefined if pointer is implicitly converted\iref{conv.ptr} to a pointer -to a virtual base class. +undefined if the pointer is implicitly converted\iref{conv.ptr} to a pointer +to a virtual base class (or base class of a virtual base class). \pnum \begin{example} @@ -178,8 +184,9 @@ \end{codeblock} \end{example} +\ubdescription{lifetime.outside.pointer.dynamic.cast} + \pnum -\ubxref{lifetime.outside.pointer.dynamic.cast} For a pointer pointing to an object outside of its lifetime, behavior is undefined if the pointer is used as the operand of a \tcode{dynamic_cast}\iref{expr.dynamic.cast}. @@ -199,8 +206,9 @@ \end{codeblock} \end{example} +\ubdescription{lifetime.outside.glvalue.access} + \pnum -\ubxref{lifetime.outside.glvalue.access} Behavior is undefined if a glvalue referring to an object outside of its lifetime is used to access the object. @@ -217,8 +225,9 @@ \end{codeblock} \end{example} +\ubdescription{lifetime.outside.glvalue.member} + \pnum -\ubxref{lifetime.outside.glvalue.member} Behavior is undefined if a glvalue referring to an object outside of its lifetime is used to call a non-static member function of the object. @@ -238,8 +247,9 @@ \end{codeblock} \end{example} +\ubdescription{lifetime.outside.glvalue.virtual} + \pnum -\ubxref{lifetime.outside.glvalue.virtual} Behavior is undefined if a glvalue referring to an object outside of its lifetime is bound to a reference to a virtual base class. @@ -258,8 +268,9 @@ \end{codeblock} \end{example} +\ubdescription{lifetime.outside.glvalue.dynamic.cast} + \pnum -\ubxref{lifetime.outside.glvalue.dynamic.cast} Behavior is undefined if a glvalue referring to an object outside of its lifetime is used as the operand of a \keyword{dynamic_cast} or as the operand of \keyword{typeid}. @@ -279,10 +290,13 @@ \end{codeblock} \end{example} +\ubdescription{original.type.implicit.destructor} + \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. +The behavior is undefined if +a non-trivial implicit destructor call +occurs when the type of the object inhabiting the associated storage +is not the original type associated with that storage. \pnum \begin{example} @@ -300,9 +314,9 @@ \end{codeblock} \end{example} +\ubdescription{creating.within.const.complete.obj} \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 @@ -324,10 +338,9 @@ \end{codeblock} \end{example} -\rSec2[ub.basic.indet]{Indeterminate and erroneous values} +\ubdescription{basic.indet.value} \pnum -\ubxref{basic.indet.value} When the result of an evaluation is an indeterminate value (but not just an erroneous value) @@ -343,16 +356,14 @@ \end{codeblock} \end{example} -\rSec2[ub.basic.stc.dynamic]{Dynamic storage Duration} +\ubdescription{basic.stc.alloc.dealloc.constraint} \pnum -\ubxref{basic.stc.alloc.dealloc.constraint} If the behavior of an allocation or deallocation function does not satisfy the semantic constraints specified in~\ref{basic.stc.dynamic.allocation} and~\ref{basic.stc.dynamic.deallocation}. the behavior is undefined. - \pnum \begin{example} \begin{codeblock} @@ -370,8 +381,9 @@ \end{codeblock} \end{example} +\ubdescription{basic.stc.alloc.dealloc.throw} + \pnum -\ubxref{basic.stc.alloc.dealloc.throw} If a call to a deallocation function terminates by throwing an exception the behavior is undefined. @@ -389,12 +401,9 @@ \end{codeblock} \end{example} - - -\rSec2[ub.basic.stc.alloc.zero.dereference]{Zero-sized allocation dereference} +\ubdescription{basic.stc.alloc.zero.dereference} \pnum -\ubxref{basic.stc.alloc.zero.dereference} The pointer returned when invoking an allocation function with a size of zero cannot be dereferenced. @@ -409,17 +418,16 @@ \end{codeblock} \end{example} -\rSec2[ub.basic.compound]{Compound types} +\ubdescription{basic.compound.invalid.pointer} \pnum -\ubxref{basic.compound.invalid.pointer} Indirection or the invocation of a deallocation function with a pointer value referencing storage that has been freed has undefined behavior. (Most other uses of such a pointer have -implemention-defined behavior.) +implementation-defined behavior.) \pnum \begin{example} @@ -434,11 +442,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.intro.execution]{Sequential execution} +\ubdescription{intro.execution.unsequenced.modification} \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 @@ -457,10 +463,9 @@ \end{codeblock} \end{example} -\rSec2[ub.intro.races]{Data races} +\ubdescription{intro.races.data} \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~\ref{intro.races}. Any such data race results in undefined behavior. @@ -475,10 +480,9 @@ \end{codeblock} \end{example} -\rSec2[ub.intro.progress]{Forward progress} +\ubdescription{intro.progress.stops} \pnum -\ubxref{intro.progress.stops} The behavior is undefined if a thread of execution that has not terminated stops making execution steps. @@ -498,10 +502,9 @@ \end{codeblock} \end{example} -\rSec2[ub.basic.start.main]{main function} +\ubdescription{basic.start.main.exit.during.destruction} \pnum -\ubxref{basic.start.main.exit.during.destruction} If \tcode{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. @@ -515,18 +518,16 @@ ~Exiter() { std::exit(0); } }; -Exiter ex; // +Exiter ex; int main() {} // undefined behavior when destructor of static variable \tcode{ex} is called it will call \tcode{std::exit} \end{codeblock} \end{example} - -\rSec2[ub.basic.start.term]{Termination} +\ubdescription{basic.start.term.use.after.destruction} \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 @@ -549,7 +550,7 @@ }; C c; -B b; +B b; // call to `f()` in constructor begins lifetime of \tcode{a} int main() {} // undefined behavior, static objects are destructed in reverse order, in this case \tcode{a} then \tcode{b} and @@ -558,25 +559,24 @@ \end{codeblock} \end{example} +\ubdescription{basic.start.term.signal.handler} \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} - +%WORDINGTODO: JMB/TD: This is really a general precondition imposed on the Standard +%Library, not a piece of core language undefined behavior. It is also currently +%missing an example. Should we retain this UB? Should we have a core issue +%to move this wording into the library section somewhere? +%WORDINGTODO: SY: If we keep this, we should have an example. \rSec1[ub.expr]{\ref{expr}: Expressions} -\rSec2[ub.expr.eval]{Result of Expression not Mathematically Defined/out of Range} + +\ubdescription{expr.expr.eval} \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. @@ -585,20 +585,21 @@ \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 + // 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} +\ubdescription{expr.basic.lvalue.strict.aliasing.violation} \pnum -\ubxref{expr.basic.lvalue.strict.aliasing.violation} If a program attempts to access\iref{defns.access} the stored value of an object whose dynamic type is $T$ through a glvalue whose type is not -similar\iref{conv.rval} to $T$ (or its corresponding signed or unsigned types) +similar\iref{conv.qual} to $T$ (or its corresponding signed or unsigned types) the behavior is undefined. \pnum @@ -619,9 +620,11 @@ \end{codeblock} \end{example} +\ubdescription{expr.basic.lvalue.union.initialization} + \pnum -\ubxref{expr.basic.lvalue.union.initialization} -If a program invokes a defaulted copy/move constructor or copy/move assignment +If a program invokes a defaulted copy/move constructor or +defaulted copy/move assignment operator of a union with an argument that is not an object of a similar type within its lifetime, the behavior is undefined. @@ -637,10 +640,9 @@ \end{codeblock} \end{example} -\rSec2[ub.expr.type]{Type} +\ubdescription{expr.type.reference.lifetime} \pnum -\ubxref{expr.type.reference.lifetime} Evaluating a reference when an equivalent use of a pointer denoting the same object would be invalid has undefined behavior. @@ -657,10 +659,9 @@ \end{codeblock} \end{example} -\rSec2[ub.conv.lval]{Lvalue-to-rvalue conversion} +\ubdescription{conv.lval.valid.representation} \pnum -\ubxref{conv.lval.valid.representation} Performing an lvalue-to-rvalue conversion on an object whose @@ -680,11 +681,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.conv.double]{Floating-point conversions} +\ubdescription{conv.double.out.of.range} \pnum -\ubxref{conv.double.out.of.range} Converting a floating point value to a type that cannot represent the value is undefined behavior. \pnum @@ -697,56 +696,54 @@ 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 + // representable 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} +\ubdescription{conv.fpint.int.not.represented} \pnum -\ubxref{conv.fpint.int.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. +When converting a value of integer or unscoped enumeration type to a +floating-point type, 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 + unsigned long long x2 = -1; + float f = x2; // undefined behavior on systems where \tcode{f} does not include + // a representation for infinity and the maximum value for \tcode{float} + // is smaller than the maximum value for \tcode{unsigned long long}. } \end{codeblock} \end{example} +\ubdescription{conv.fpint.float.not.represented} + \pnum -\ubxref{conv.fpint.float.not.represented} -When converting a value of integer or unscoped enumeration type to a -floating-point type, if the value is not representable in the destination type -it is undefined behavior. +When converting a floating-point value to an integer type, if +the value is not representable in the destination type it is undefined behavior. \pnum \begin{example} \begin{codeblock} +#include + int main() { - __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 + // 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 } \end{codeblock} \end{example} -\rSec2[ub.conv.ptr]{Pointer conversions} +\ubdescription{conv.ptr.virtual.base} \pnum -\ubxref{conv.ptr.virtual.base} Converting a pointer to a derived class \tcode{D} to @@ -769,10 +766,9 @@ \end{codeblock} \end{example} -\rSec2[ub.conv.mem]{Pointer-to-member conversions} +\ubdescription{conv.member.missing.member} \pnum -\ubxref{conv.member.missing.member} The conversion of a pointer to a member of a base class to a pointer to member of a derived class @@ -794,13 +790,12 @@ \end{codeblock} \end{example} - - -\rSec2[ub.expr.call]{Function call} +\ubdescription{expr.call.different.type} \pnum -\ubxref{expr.call.different.type} -Calling a function through an expression whose function type is different from the function type of the called +Calling a function through an expression whose +function type is not call-compatible with +the function type of the called function's definition results in undefined behavior. \pnum @@ -820,10 +815,9 @@ \end{codeblock} \end{example} -\rSec2[ub.expr.ref]{Class member access} +\ubdescription{expr.ref.member.not.similar} \pnum -\ubxref{expr.ref.member.not.similar} If \tcode{E2} is a non-static member and the result of \tcode{E1} is an object whose type is not similar\iref{conv.qual} to the type of \tcode{E1}, the behavior is undefined. @@ -840,11 +834,12 @@ \end{codeblock} \end{example} -\rSec2[ub.expr.dynamic.cast]{Dynamic cast} +%WORDINGTODO: SY: The wording is not great, I don't have a good suggestion but we should +%get some opinions +\ubdescription{expr.dynamic.cast.pointer.lifetime} \pnum -\ubxref{expr.dynamic.cast.pointer.lifetime} Evaluating a \keyword{dynamic_cast} on a non-null pointer that points to an object (of polymorphic type) of the wrong type or to an object not within its lifetime has undefined behavior. @@ -860,9 +855,9 @@ \end{codeblock} \end{example} +\ubdescription{expr.dynamic.cast.glvalue.lifetime} \pnum -\ubxref{expr.dynamic.cast.glvalue.lifetime} Evaluating a \keyword{dynamic_cast} on a reference that denotes an object (of polymorphic type) of the wrong type or an object not within its lifetime has undefined behavior. @@ -878,17 +873,18 @@ \end{codeblock} \end{example} -\rSec2[ub.expr.static.cast]{Static cast} +\ubdescription{expr.static.cast.base.class} \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. +A glvalue of type \tcode{B} +can be cast to the type ``reference to \tcode{D}'' +if \tcode{B} is a base class of \tcode{D}, +otherwise the behavior is undefined. \pnum \begin{example} \begin{codeblock} -truct B {}; +struct B {}; struct D1 : B {}; struct D2 : B {}; @@ -900,8 +896,9 @@ \end{codeblock} \end{example} +\ubdescription{expr.static.cast.enum.outside.range} + \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. @@ -912,13 +909,14 @@ enum A { e1 = 1, e2 }; void f() { - enum A a = static_cast(4); // undefined behavior, 4 is not with the range of enumeration values + enum A a = static_cast(4); // undefined behavior, 4 is not within the range of enumeration values } \end{codeblock} \end{example} +\ubdescription{expr.static.cast.fp.outside.range} + \pnum -\ubxref{expr.static.cast.fp.outside.range} An explicit conversion of a floating-point value that is outside the range of the target type has undefined behavior. @@ -926,7 +924,7 @@ \pnum \begin{example} If \tcode{float} does not adhere to \IsoFloatUndated{} -and cannot represent positive infinty, +and cannot represent positive infinity, a sufficiently large \tcode{double} value will be outside the (finite) range of \tcode{float}. \begin{codeblock} @@ -938,10 +936,12 @@ \end{codeblock} \end{example} +\ubdescription{expr.static.cast.downcast.wrong.derived.type} \pnum -\ubxref{expr.static.cast.downcast.wrong.derived.type} -Down-casting to the wrong derived type is undefined behavior. +Casting from a pointer to a base class to a pointer to a derived class +when there is no enclosing object of that derived class at the +specified location has undefined behavior. \pnum \begin{example} @@ -957,11 +957,18 @@ \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. +\ubdescription{expr.static.cast.does.not.contain.original.member} +\pnum +A +pointer to member of derived class D +can be cast to +a pointer to member of base class B +(with certain restrictions on cv qualifiers) +as long as B contains the original member, +or is a base or derived class of the class +containing the original member; +otherwise the behavior is undefined. \pnum \begin{example} @@ -983,10 +990,9 @@ \end{codeblock} \end{example} -\rSec2[ub.expr.unary.op]{Unary operators} +\ubdescription{expr.unary.dereference} \pnum -\ubxref{expr.unary.dereference} Dereferencing a pointer that does not point to an object or function has undefined behavior. @@ -1001,11 +1007,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.expr.new]{New} +\ubdescription{expr.new.non.allocating.null} \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. @@ -1028,16 +1032,14 @@ int main() { char *p = nullptr; - A *a = new (p) A; // undefined behavior, non-allocting new returning nullptr + A *a = new (p) A; // undefined behavior, non-allocating new returning nullptr } \end{codeblock} \end{example} - -\rSec2[ub.expr.delete]{Delete} +\ubdescription{expr.delete.mismatch} \pnum -\ubxref{expr.delete.mismatch} Using array delete on the result of a single object new expression is undefined behavior. \pnum @@ -1048,8 +1050,9 @@ \end{codeblock} \end{example} +\ubdescription{expr.delete.array.mismatch} + \pnum -\ubxref{expr.delete.array.mismatch} Using single object delete on the result of an array new expression is undefined behavior. \pnum @@ -1060,11 +1063,11 @@ \end{codeblock} \end{example} +\ubdescription{expr.delete.dynamic.type.differ} \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 +type and the selected deallocation function 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. @@ -1086,9 +1089,9 @@ \end{codeblock} \end{example} +\ubdescription{expr.delete.dynamic.array.dynamic.type.differ} \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 @@ -1112,11 +1115,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.expr.mptr.oper]{Pointer-to-member operators} +\ubdescription{expr.mptr.oper.not.contain.member} \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. @@ -1137,10 +1138,10 @@ \end{codeblock} \end{example} +\ubdescription{expr.mptr.oper.member.func.null} \pnum -\ubxref{expr.mptr.oper.member.func.null} -If the second operand is the null +If the second operand in a \tcode{.*} expression is the null member pointer value\iref{conv.mem}, the behavior is undefined. \pnum @@ -1158,11 +1159,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.expr.mul]{Multiplicative operators} +\ubdescription{expr.mul.div.by.zero} \pnum -\ubxref{expr.mul.div.by.zero} Division by zero is undefined behavior. \pnum @@ -1172,16 +1171,20 @@ 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 + // representable values are [-inf,+inf] this would not be UB } \end{codeblock} \end{example} +\ubdescription{expr.mul.representable.type.result} + \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. +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. \pnum \begin{example} @@ -1189,17 +1192,16 @@ #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 + int x = std::numeric_limits::min() / -1; + // Assuming LP64 $-2,147,483,648$ which when divided by $-1$ + // gives us $2,147,483,648$ which is not representable by int } \end{codeblock} \end{example} -\rSec2[ub.expr.add]{Additive operators} +\ubdescription{expr.add.out.of.bounds} \pnum -\ubxref{expr.add.out.of.bounds} Creating an out of bounds pointer is undefined behavior. \pnum @@ -1224,8 +1226,9 @@ \end{codeblock} \end{example} +\ubdescription{expr.add.sub.diff.pointers} + \pnum -\ubxref{expr.add.sub.diff.pointers} Subtracting pointers that are not part of the same array is undefined behavior. \pnum @@ -1242,9 +1245,11 @@ \end{codeblock} \end{example} +\ubdescription{expr.add.not.similar} + \pnum -\ubxref{expr.add.not.similar} -For addition or subtraction, if the expressions P or Q have type ``pointer to cv T'', where T and the array +For addition or subtraction of two expressions P and Q, +if 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 @@ -1271,11 +1276,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.expr.shift]{Shift operators} +\ubdescription{expr.shift.neg.and.width} \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 @@ -1289,11 +1292,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.expr.assign]{Assignment and compound assignment operators} +\ubdescription{expr.assign.overlap} \pnum -\ubxref{expr.assign.overlap} Overlap in the storage between the source and destination may result in undefined behavior. \pnum @@ -1305,14 +1306,14 @@ \end{codeblock} \end{example} -\rSec1[ub.stmt.stmt]{\ref{stmt}: Statements} +\rSec1[ub.stmt]{\ref{stmt}: Statements} -\rSec2[ub.stmt.return]{The return statement} +\ubdescription{stmt.return.flow.off} \pnum -\ubxref{stmt.return.flow.off} Flowing off the end of a function other -than main or a coroutine results in undefined behavior. +than main or a coroutine results in undefined behavior if the return type +is not \cv{}~\keyword{void}. \pnum \begin{example} @@ -1325,16 +1326,17 @@ 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 + // without a return statement } \end{codeblock} \end{example} -\rSec2[ub.return.coroutine]{The co_return statement} +\ubdescription{stmt.return.coroutine.flow.off} \pnum -\ubxref{stmt.return.coroutine.flow.off} -Falling off the end of a coroutine function body that does not return void is undefined behavior. +Flowing off the end of a coroutine function body +that does not return void +has undefined behavior. \pnum \begin{example} @@ -1364,11 +1366,11 @@ }; struct resumable::promise_type { - using coro_handle = std::experimental::coroutine_handle; + using coro_handle = std::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(); } + auto initial_suspend() { return std::suspend_always(); } + auto final_suspend() noexcept { return std::suspend_always(); } void unhandled_exception() { std::terminate(); } void return_value(const char* string) { string_ = string; } }; @@ -1379,7 +1381,7 @@ resumable foo() { std::cout << "Hello" << std::endl; - co_await std::experimental::suspend_always(); + co_await std::suspend_always(); // undefined behavior, falling off the end of coroutine that does not return void } @@ -1392,10 +1394,9 @@ \end{codeblock} \end{example} -\rSec2[ub.stmt.dcl]{Declaration statement} +\ubdescription{stmt.dcl.local.static.init.recursive} \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. @@ -1409,15 +1410,11 @@ \end{codeblock} \end{example} +\rSec1[ub.dcl]{\ref{dcl}: Declarations} - - -\rSec1[ub.dcl.dcl]{\ref{dcl}: Declarations} - -\rSec2[ub.dcl.type.cv]{The cv-qualifiers} +\ubdescription{dcl.type.cv.modify.const.obj} \pnum -\ubxref{dcl.type.cv.modify.const.obj} Any attempt to modify a const object during its lifetime results in undefined behavior. @@ -1430,9 +1427,9 @@ \end{codeblock} \end{example} +\ubdescription{dcl.type.cv.access.volatile} \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 @@ -1446,10 +1443,9 @@ \end{codeblock} \end{example} -\rSec2[ub.dcl.ref]{References} +\ubdescription{dcl.ref.incompatible.function} \pnum -\ubxref{dcl.ref.incompatible.function} Initializing a reference to a function with a value that is a function that is not call-compatible\iref{expr.call} @@ -1464,8 +1460,9 @@ \end{codeblock} \end{example} +\ubdescription{dcl.ref.incompatible.type} + \pnum -\ubxref{dcl.ref.incompatible.type} Initializing a reference to an object with a value that is not type-accessible\iref{basic.lval} through @@ -1480,8 +1477,9 @@ \end{codeblock} \end{example} +\ubdescription{dcl.ref.uninitialized.reference} + \pnum -\ubxref{dcl.ref.uninitialized.reference} Evaluating a reference prior to initializing that reference has undefined behavior. @@ -1496,21 +1494,15 @@ \end{codeblock} \end{example} - - - -\rSec2[ub.dcl.fct.def.coroutine]{Coroutine definitions} +\ubdescription{dcl.fct.def.coroutine.resume.not.suspended} \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; +#include struct minig { struct promise_type { @@ -1557,17 +1549,15 @@ \end{codeblock} \end{example} +\ubdescription{dcl.fct.def.coroutine.destroy.not.suspended} \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; +#include struct minig { struct promise_type { @@ -1614,11 +1604,10 @@ \end{codeblock} \end{example} -\rSec2[ub.dcl.attr.assume]{Assumption attribute} +\ubdescription{dcl.attr.assume.false} \pnum -\ubxref{dcl.attr.assume.false} -If am assumption expression would not evaluate to true at the point where it +If an assumption expression would not evaluate to true at the point where it appears the behavior is undefined. \pnum @@ -1635,11 +1624,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.dcl.attr.noreturn]{Noreturn attribute} +\ubdescription{dcl.attr.noreturn.eventually.returns} \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. @@ -1657,14 +1644,14 @@ \end{codeblock} \end{example} - \rSec1[ub.class]{\ref{class}: Classes} -\rSec2[ub.class.dtor]{Destructors} +\ubdescription{class.dtor.no.longer.exists} \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 +Once a destructor is invoked for an object, +the object's lifetime has ended; +the behavior is undefined if the destructor is invoked for an object whose lifetime has ended. \pnum @@ -1676,16 +1663,14 @@ int main() { A a; - a.~A(); // undefined behavior, destructor will be invoked again at scope exit -} + a.~A(); +} // undefined behavior, lifetime of \tcode{a} already ended before implicit destructor \end{codeblock} \end{example} - -\rSec2[ub.class.abstract]{Abstract classes} +\ubdescription{class.abstract.pure.virtual} \pnum -\ubxref{class.abstract.pure.virtual} Calling a pure virtual function from a constructor or destructor in an abstract class is undefined behavior. \pnum @@ -1704,11 +1689,9 @@ \end{codeblock} \end{example} - -\rSec2[ub.class.base.init]{Initializing bases and members} +\ubdescription{class.base.init.mem.fun} \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 @@ -1725,7 +1708,7 @@ public: int f(); B() - : A(f()), // undefined: calls member function but base Ac not yet initialized + : A(f()), // undefined: calls member function but base A not yet initialized j(f()) {} // well-defined: bases are all initialized }; @@ -1745,15 +1728,11 @@ \end{codeblock} \end{example} - -\rSec2[ub.class.cdtor]{Construction and destruction} +\ubdescription{class.cdtor.before.ctor} \pnum -\ubxref{class.cdtor.before.ctor} For an object with a non-trivial constructor, referring to any non-static member or base class of the object -before the constructor begins execution results in undefined behavior. For an object with a non-trivial -destructor, referring to any non-static member or base class of the object after the destructor finishes execution -results in undefined behavior. +before the constructor begins execution results in undefined behavior. \pnum \begin{example} @@ -1797,9 +1776,11 @@ \end{codeblock} \end{example} +%WORDINGTODO: CM: Can this example be shortened? + +\ubdescription{class.cdtor.after.dtor} \pnum -\ubxref{class.cdtor.after.dtor} For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution @@ -1826,8 +1807,9 @@ \end{codeblock} \end{example} +\ubdescription{class.cdtor.convert.pointer} + \pnum -\ubxref{class.cdtor.convert.pointer} When converting a pointer to a base class of an object, construction must have started and destruction must not have finished otherwise this is undefined behavior. @@ -1853,8 +1835,9 @@ \end{codeblock} \end{example} +\ubdescription{class.cdtor.form.pointer} + \pnum -\ubxref{class.cdtor.form.pointer} When forming a pointer to a direct non-static member of a class, construction must have started @@ -1875,8 +1858,9 @@ \end{codeblock} \end{example} +\ubdescription{class.cdtor.virtual.not.x} + \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 @@ -1917,9 +1901,9 @@ \end{codeblock} \end{example} +\ubdescription{class.cdtor.typeid} \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. @@ -1948,9 +1932,9 @@ \end{codeblock} \end{example} +\ubdescription{class.cdtor.dynamic.cast} \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} @@ -1979,12 +1963,11 @@ \end{codeblock} \end{example} - \rSec1[ub.temp]{\ref{temp}: Templates} -\rSec2[ub.temp.inst]{Implicit instantiation} + +\ubdescription{temp.inst.inf.recursion} \pnum -\ubxref{temp.inst.inf.recursion} The result of an infinite recursion in template instantiation is undefined. \pnum @@ -2004,13 +1987,11 @@ \end{codeblock} \end{example} - \rSec1[ub.except]{\ref{except}: Exception handling} -\rSec2[ub.except.handle]{Handling an exception} +\ubdescription{except.handle.handler.ctor.dtor} \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.