From 4a6c0226df70d948781158624e00c9dc3b7b1004 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Tue, 10 Feb 2026 13:10:12 -0500 Subject: [PATCH 01/10] [ub] and [ifndr] fixes and updates after feedback from Christof Meerwald --- source/ifndr.tex | 6 ++++++ source/ub.tex | 43 +++++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/source/ifndr.tex b/source/ifndr.tex index 0185e2ccfa..6fa7ecaac9 100644 --- a/source/ifndr.tex +++ b/source/ifndr.tex @@ -200,6 +200,12 @@ \pnum \ifndrxref{dcl.attr.noreturn.trans.unit.mismatch} +No diagnostic is requried 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 \begin{example} \begin{codeblocktu}{Translation unit \#1} [[noreturn]] void f() {} diff --git a/source/ub.tex b/source/ub.tex index d913666ede..48760423f9 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -161,8 +161,8 @@ \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} @@ -282,7 +282,7 @@ \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 original type occupies the storage is undefined. \pnum \begin{example} @@ -800,7 +800,9 @@ \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 @@ -941,7 +943,9 @@ \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} @@ -959,8 +963,15 @@ \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. +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 @@ -1244,7 +1255,8 @@ \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 @@ -1312,7 +1324,8 @@ \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} @@ -1334,7 +1347,9 @@ \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} @@ -1618,7 +1633,7 @@ \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 @@ -1751,9 +1766,7 @@ \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,6 +1810,8 @@ \end{codeblock} \end{example} +%TODO: CM: Can this example be shortened? + \pnum \ubxref{class.cdtor.after.dtor} From da6a8de2dca8a233b857dc3df96b8ba3de8d090a Mon Sep 17 00:00:00 2001 From: jberne4 Date: Tue, 10 Feb 2026 13:58:29 -0500 Subject: [PATCH 02/10] [*] made sure ubdef and ifndrdef do not have preceding whitespaces and come before full stops --- source/basic.tex | 6 +++--- source/declarations.tex | 4 ++-- source/expressions.tex | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index 5801ed63ac..8d605c1b2e 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -3769,7 +3769,7 @@ using the alignment specifier\iref{dcl.align}. Attempting to create an object\iref{intro.object} in storage that does not meet the alignment requirements of the object's type -is undefined behavior.\ubdef{basic.align.object.alignment} +is undefined behavior\ubdef{basic.align.object.alignment}. \pnum A \defnadj{fundamental}{alignment} is represented by an alignment @@ -4514,7 +4514,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 @@ -4637,7 +4637,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. diff --git a/source/declarations.tex b/source/declarations.tex index b0de3c469e..6060a79671 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -3274,7 +3274,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} @@ -3292,7 +3292,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&); diff --git a/source/expressions.tex b/source/expressions.tex index 9336580eff..51db653ac8 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 @@ -4507,14 +4507,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 From 1908a192a62266aa26573dba1f61d99c8ef45f99 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Tue, 10 Feb 2026 14:08:09 -0500 Subject: [PATCH 03/10] [ub] comment about library ub in the core wording --- source/ub.tex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/ub.tex b/source/ub.tex index 48760423f9..3a26d0ff30 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -571,6 +571,10 @@ \end{codeblock} \end{example} +%TODO: 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? \rSec1[ub.expr]{\ref{expr}: Expressions} \rSec2[ub.expr.eval]{Result of Expression not Mathematically Defined/out of Range} From 0e1f04a1c55fdc310417498a3068ccbe070f78b0 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Tue, 10 Feb 2026 15:25:29 -0500 Subject: [PATCH 04/10] [ub] feedback from Shafik on ub annex --- source/ub.tex | 66 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/source/ub.tex b/source/ub.tex index 3a26d0ff30..10fe4ce995 100644 --- a/source/ub.tex +++ b/source/ub.tex @@ -90,6 +90,11 @@ \end{codeblock} \end{example} +%TODO: 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 ..." + \rSec2[ub.basic.align]{Object alignment} \pnum @@ -105,7 +110,9 @@ 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} @@ -281,8 +288,10 @@ \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 is undefined. +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} @@ -515,7 +524,7 @@ ~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} @@ -549,7 +558,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 @@ -575,6 +584,7 @@ %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? +%TODO: 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} @@ -589,9 +599,11 @@ \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} @@ -625,7 +637,8 @@ \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. @@ -701,7 +714,7 @@ 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} @@ -721,10 +734,10 @@ #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 + // 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 + int x1 = d; // undefined behavior $2,147,483,647 + 1$ is not representable as int } \end{codeblock} \end{example} @@ -846,6 +859,9 @@ \end{codeblock} \end{example} +%TODO: SY: The wording is not great, I don't have a good suggestion but we should +%get some opinions + \rSec2[ub.expr.dynamic.cast]{Dynamic cast} @@ -894,7 +910,7 @@ \pnum \begin{example} \begin{codeblock} -truct B {}; +struct B {}; struct D1 : B {}; struct D2 : B {}; @@ -1079,7 +1095,7 @@ \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. @@ -1155,7 +1171,7 @@ \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 @@ -1204,9 +1220,9 @@ #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} @@ -1683,7 +1699,9 @@ \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 @@ -1695,8 +1713,8 @@ 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} @@ -1744,7 +1762,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 }; @@ -1770,7 +1788,7 @@ \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. +before the constructor begins execution results in undefined behavior. \pnum \begin{example} From 3bb833c90f18221638fef0b67041d6033025094a Mon Sep 17 00:00:00 2001 From: jberne4 Date: Wed, 11 Feb 2026 12:15:53 -0500 Subject: [PATCH 05/10] [ifndr] more feebdack from Shafik --- source/ifndr.tex | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/source/ifndr.tex b/source/ifndr.tex index 6fa7ecaac9..d8474847b9 100644 --- a/source/ifndr.tex +++ b/source/ifndr.tex @@ -518,6 +518,8 @@ \end{codeblock} \end{example} +%TODO: SY, JMB: We need to produce an example for this case. + \rSec2[ifndr.temp.dep.res]{Dependent name resolution} \rSec3[ifndr.temp.point]{Point of instantiation} @@ -584,18 +586,27 @@ \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} + +%TODO: JMB: Someone should confirm that the comments correctly describe why +%this example is ill-formed. From a51229c12541433d43d3e1026f8f4f3c456d16c7 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Wed, 18 Feb 2026 12:25:26 -0500 Subject: [PATCH 06/10] [ifndr]: added missing entries brought up by Alisdair --- source/ifndr.tex | 55 +++++++++++++++++++++++++++++++++++++++++ source/preprocessor.tex | 7 +++--- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/source/ifndr.tex b/source/ifndr.tex index d8474847b9..480c890d15 100644 --- a/source/ifndr.tex +++ b/source/ifndr.tex @@ -610,3 +610,58 @@ %TODO: JMB: Someone should confirm that the comments correctly describe why %this example is ill-formed. + + +\rSec1[ifndr.cpp]{\ref{cpp}: Preprocessing directives} + +\rSec2[ifndr.cpp.cond]{Conditional inclusion} + +\pnum +\ifndrxref{cpp.cond.defined.after.macro} +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} +\end{codeblock} +\end{example} + +%TODO: JMB: An example that reproduces this case should be provided (preferably +% one that illuminates why the diagnostic might not be produced) + +\pnum +\ifndrxref{cpp.cond.defined.malformed} +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} +\end{codeblock} +\end{example} + +%TODO: JMB: An example that reproduces this case should be provided (preferably +% one that illuminates why the diagnostic might not be produced) + + +\rSec2[ifndr.cpp.include]{Source file inclusion} + +\pnum +\ifndrxref{cpp.include.malformed.headername} +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} +\end{codeblock} +\end{example} + +%TODO: JMB: An example that reproduces this case should be provided (preferably +% one that illuminates why the diagnostic might not be produced) diff --git a/source/preprocessor.tex b/source/preprocessor.tex index b9809ae664..e00d38e7da 100644 --- a/source/preprocessor.tex +++ b/source/preprocessor.tex @@ -542,12 +542,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 @@ -772,7 +772,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} From a2167fade1a3feec3c852bcec5ca5549ba127fe1 Mon Sep 17 00:00:00 2001 From: notadragon Date: Thu, 19 Feb 2026 22:25:53 -0500 Subject: [PATCH 07/10] [ifndr]: started with shafik's suggested examples and refined them a bit --- source/ifndr.tex | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/ifndr.tex b/source/ifndr.tex index 480c890d15..92601dc280 100644 --- a/source/ifndr.tex +++ b/source/ifndr.tex @@ -624,12 +624,13 @@ \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} -%TODO: JMB: An example that reproduces this case should be provided (preferably -% one that illuminates why the diagnostic might not be produced) - \pnum \ifndrxref{cpp.cond.defined.malformed} If the \tcode{defined} unary operator is used when it @@ -640,13 +641,14 @@ \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} -%TODO: JMB: An example that reproduces this case should be provided (preferably -% one that illuminates why the diagnostic might not be produced) - - \rSec2[ifndr.cpp.include]{Source file inclusion} \pnum @@ -660,8 +662,6 @@ \pnum \begin{example} \begin{codeblock} +#include `` // Ill-formed no diagnoatic required, does not match one of the two allowable forms \end{codeblock} \end{example} - -%TODO: JMB: An example that reproduces this case should be provided (preferably -% one that illuminates why the diagnostic might not be produced) From db9b3b1bbfca61a47dd77e3fb412c78cf4b2e2cb Mon Sep 17 00:00:00 2001 From: notadragon Date: Thu, 19 Feb 2026 22:30:25 -0500 Subject: [PATCH 08/10] [ifndr] added comment on possibly non-NDR entry temp.names.sat.constraints --- source/ifndr.tex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/ifndr.tex b/source/ifndr.tex index 92601dc280..dd6c66b7d0 100644 --- a/source/ifndr.tex +++ b/source/ifndr.tex @@ -479,6 +479,8 @@ \end{codeblock} \end{example} +%TODO: AM: There does not seem to be something obvious in the wording itself that makes this case NDR + \rSec2[ifndr.temp.fct]{Function templates} From 8caaf49a64a71132558c2fd217d362cc48bd2640 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Thu, 19 Feb 2026 22:48:25 -0500 Subject: [PATCH 09/10] [ifndr] fixing (hopefully) bad unary operator defined example --- source/ifndr.tex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/ifndr.tex b/source/ifndr.tex index dd6c66b7d0..cbe1d97046 100644 --- a/source/ifndr.tex +++ b/source/ifndr.tex @@ -626,7 +626,7 @@ \pnum \begin{example} \begin{codeblock} -define A defined +#define A defined #if A // Ill-formed no diagnostic required, \tcode{defined} is generated by macro replacement // in controlling expression #endif @@ -643,9 +643,9 @@ \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 +#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} From 0f3e8580772ec557cf2952cd3cc322b495d625e7 Mon Sep 17 00:00:00 2001 From: jberne4 Date: Fri, 20 Feb 2026 12:58:41 -0500 Subject: [PATCH 10/10] [ifndr]: added many missing ifndr types, removed temp.names.sat.constraints --- source/basic.tex | 10 +- source/declarations.tex | 16 +- source/ifndr.tex | 346 +++++++++++++++++++++++++++++++++++++--- source/modules.tex | 5 +- source/templates.tex | 8 +- 5 files changed, 342 insertions(+), 43 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index 8d605c1b2e..9439b62c4c 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -3076,7 +3076,8 @@ \pnum If two declarations of an entity are attached to different modules, the program is ill-formed; -no diagnostic is required if neither is reachable from the other. +no diagnostic is required +if neither is reachable from the other\ifndrdef{basic.link.entity.same.module}. \begin{example} \begin{codeblocktu}{\tcode{"decls.h"}} int f(); // \#1, attached to the global module @@ -3142,7 +3143,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} @@ -7695,7 +7697,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} @@ -8101,6 +8103,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 6060a79671..dfc3a32228 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -1064,7 +1064,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 @@ -1139,7 +1139,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. @@ -4364,7 +4364,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 @@ -4669,7 +4670,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$ @@ -7578,7 +7580,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 @@ -9299,7 +9301,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. @@ -9880,7 +9882,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/ifndr.tex b/source/ifndr.tex index cbe1d97046..bfff4e10fe 100644 --- a/source/ifndr.tex +++ b/source/ifndr.tex @@ -34,6 +34,58 @@ \rSec1[ifndr.basic]{\ref{basic}: Basics} +\rSec2[ifndr.basic.link]{Program and linkage} + +\pnum +\ifndrxref{basic.link.entity.same.module} +If an entity has multiple declarations +attached to different modules +where neither is reachable from the other +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblocktu}{\tcode{"decls.h"}} +int h(); // \#1, attached to the global module +\end{codeblocktu} +\begin{codeblocktu}{Module interface of \tcode{M}} +module; +export moodule M; +export int h(); // \#1, attached to \tcode{M} +\end{codeblocktu} +\begin{codeblocktu}{Other translation unit} +#include "decls.h" +import M; + // ill-formed, no diagnostic required, the declarations of `h` cannot + // reach one another +\end{codeblocktu} +\end{example} + +\pnum +\ifndrxref{basic.link.consistent.types} +Multiple declarations of an entity must be consistent, +no diagnostic is required if neither declaration is reachable +from the other. + +\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} + \rSec2[ifndr.basic.def.odr]{One-definition rule} \pnum @@ -81,6 +133,47 @@ \end{example} +\rSec2[ifndr.basic.contract]{Contract assertions} + +\rSec3[ifndr.basic.contract.general]{General} + +\pnum +\ifndrxref{basic.contract.vastart.contract.predicate} +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} + +\rSec3[ifndr.basic.contract.handler]{Contract-violation handler} + +\pnum +\ifndrxref{basic.contract.handler.replacing.nonreplaceable} +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} \pnum @@ -178,7 +271,126 @@ \rSec1[ifndr.dcl.dcl]{\ref{dcl}: Declarations} -\rSec2[ifndr.dcl.align]{Alignment specifier} +\rSec2[ifndr.dcl.spec]{Specifiers} + +\rSec3[ifndr.dcl.constinit]{The \keyword{constinit} specifier} + +\pnum +\ifndrxref{dcl.constinit.specifier.not.reachable} +If the initializing declaration +of a variable that has the \tcode{constinit} specifier +applied to declarations not reachable from +that initializing declaration +the program is ill-formed, no diagnostic required. + +\pnum +\begin{example} +\begin{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + +\rSec3[ifndr.dcl.inline]{The \keyword{inline} specifier} + +\pnum +\ifndrxref{dcl.inline.missing.on.definition} +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{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + +\rSec2[ifndr.dcl.fct]{Functions} + +\rSec3[ifndr.dcl.fct.default]{Default arguments} + +\pnum +\ifndrxref{dcl.fct.default.inline.same.defaults} +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{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + +\rSec2[ifndr.dcl.contract]{Function contract specifiers} + +\rSec3[ifndr.dcl.contract.func]{General} + +\pnum +\ifndrxref{dcl.contract.func.mismatched.contract.specifiers} +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{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + +\rSec2[ifndr.dcl.fct.def]{Function definitions} + +\rSec3[ifndr.dcl.fct.def.replace]{Replaceable function definitions} + +\pnum +\ifndrxref{dcl.fct.def.replace.bad.replacement} +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. + +\pnum +\begin{example} +\begin{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + +\rSec2[ifndr.dcl.link]{Linkage specifications} + +\pnum +\ifndrxref{dcl.link.mismatched.language.linkage} +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} + +\rSec2[ifndr.dcl.attr]{Attributes} + +\rSec3[ifndr.dcl.align]{Alignment specifier} \pnum \ifndrxref{dcl.align.diff.translation.units} @@ -196,7 +408,24 @@ \end{codeblocktu} \end{example} -\rSec2[ifndr.dcl.attr.noreturn]{Noreturn attribute} +\rSec3[ifndr.dcl.attr.indet]{Indeterminate storage} + +\pnum +\ifndrxref{dcl.attr.indet.mismatched.declarations} +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{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + +\rSec3[ifndr.dcl.attr.noreturn]{Noreturn attribute} \pnum \ifndrxref{dcl.attr.noreturn.trans.unit.mismatch} @@ -251,6 +480,38 @@ \end{codeblock} \end{example} +\pnum +\ifndrxref{module.unit.unexported.module.partition} +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{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + +\rSec2[ifndr.module.private.frag]{Private module fragment} + +\pnum +\ifndrxref{module.private.frag.other.module.units} +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{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + \rSec1[ifndr.class]{\ref{class}: Classes} @@ -432,6 +693,9 @@ \end{example} +%TODO: There are two earlier "no diagnostic requireds" in this section, i am +% not sure if they a really are worth calling distinct cases of ifndr. + \rSec2[ifndr.temp.spec.partial]{Partial specialization} \pnum @@ -460,27 +724,6 @@ \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} - -%TODO: AM: There does not seem to be something obvious in the wording itself that makes this case NDR - \rSec2[ifndr.temp.fct]{Function templates} @@ -522,9 +765,9 @@ %TODO: SY, JMB: We need to produce an example for this case. -\rSec2[ifndr.temp.dep.res]{Dependent name resolution} +\rSec3[ifndr.temp.dep.res]{Dependent name resolution} -\rSec3[ifndr.temp.point]{Point of instantiation} +\rSec4[ifndr.temp.point]{Point of instantiation} \pnum \ifndrxref{temp.point.diff.pt.diff.meaning} @@ -557,8 +800,27 @@ \end{codeblock} \end{example} +\rSec4[ifndr.temp.dep.candidate]{Candidate functions} + +\pnum +\ifndrxref{temp.dep.candidate.different.lookup.different} +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 +\begin{example} +\begin{codeblock} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + -\rSec2[ifndr.temp.explicit]{Explicit instantiation} +\rSec3[ifndr.temp.explicit]{Explicit instantiation} \pnum \ifndrxref{temp.explicit.decl.implicit.inst} @@ -580,6 +842,38 @@ \end{codeblock} \end{example} +\rSec3[ifndr.temp.expl.spec]{Explicit specialization} + +\pnum +\ifndrxref{temp.expl.spec.unreachable.declaration} +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} + +%TODO: JMB: produce an example + +\pnum +\ifndrxref{temp.expl.spec.missing.definition} +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} +\end{codeblock} +\end{example} + +%TODO: JMB: produce an example + \rSec2[ifndr.temp.deduct]{Template argument deduction} diff --git a/source/modules.tex b/source/modules.tex index de2fa86c66..8e7a4695da 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/templates.tex b/source/templates.tex index e50b89a394..d6c4398f16 100644 --- a/source/templates.tex +++ b/source/templates.tex @@ -984,7 +984,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); @@ -6266,7 +6266,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} @@ -7357,11 +7357,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}