Annex G (informative) Ill-formed, no diagnostic required [ifndr]


G.1 General [ifndr.general]

G.2 [lex]: Lexical conventions [ifndr.lex]

G.3 [basic]: Basics [ifndr.basic]

G.4 [expr]: Expressions [ifndr.expr]

G.5 [stmt]: Statements [ifndr.stmt]

G.6 [dcl]: Declarations [ifndr.dcl]

G.7 [module]: Modules [ifndr.module]

G.8 [class]: Classes [ifndr.class]

G.9 [over]: Overloading [ifndr.over]

G.10 [temp]: Templates [ifndr.temp]

G.11 [cpp]: Preprocessing directives [ifndr.cpp]


G.1 General [ifndr.general]

This Annex documents rules for which no diagnostic is required, called out in [intro] through [cpp] using the following phrases:
  • no diagnostic is required
  • no diagnostic required
  • a diagnostic is required only if
Each entry contains a title, a cross-reference, a summary of the circumstances, and code examples.
The code examples are not intended to exhaustively cover all possible ways of invoking that case.

G.2 [lex]: Lexical conventions [ifndr.lex]

G.2.1[ifndr:lex.name.reserved]
Specified in: [lex.name]

Using an identifier reserved for use by C++ is ill-formed, no diagnostic required.
[Example 1: int _z; // IFNDR, z is reserved because it starts with at global scope int main() { int __x; // IFNDR, x is reserved because it starts with int _Y; // IFNDR, Y is reserved because it starts with followed by a capital letter int x__y; // IFNDR, xy is reserved because it contains } — end example]

G.3 [basic]: Basics [ifndr.basic]

G.3.1[ifndr:basic.link.consistent.types]
Specified in: [basic.link]

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.
[Example 1: 

Module interface of M:void g(); // #1 void h(); // #2 template <typename T> int j; // #3

Module interface of N:int g(); // same entity as #1, different type namespace h {} // same entity as #2, not both namespaces template <int X> int j; // same entity as #3, non-equivalent template heads

Other translation unit:import M; import N; // IFNDR due to the mismatched pairs above — end example]

G.3.2[ifndr:basic.def.odr.minimum.one.def]
Specified in: [basic.def.odr]

Not having a definition for a function or variable that is odr-used from a non-discarded statement ([stmt.if]) is ill-formed, no diagnostic required.
[Example 1: auto f() { struct A {}; return A{}; } decltype(f()) g(); auto x = g(); // IFNDR, function g is used but not defined in this translation unit, and cannot // be defined in any other translation unit because its type does not have linkage — end example]

G.3.3[ifndr:basic.def.odr.injected.match]
Specified in: [basic.def.odr]

Defining a definable item D with an injected declaration ([expr.const.reflect]) in one translation unit and a definition in a different translation unit when D is not attached to a named module or neither definition is reachable from the other is ill-formed, no diagnostic required.
[Example 1: 

Translation unit #1:struct S; consteval { std::meta::define_aggregate(^^S, {}); } // #1

Translation unit #2:struct S {}; // IFNDR, definition here, injected declaration at #1 — end example]

G.3.4[ifndr:basic.def.odr.maximum.one.def]
Specified in: [basic.def.odr]

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.
[Example 1: 

Translation unit #1:export module M:A; // module partition void f() {} // #1 void g() {} // #2

Translation unit #2:export module M:B; // module partition void f() {} // IFNDR, #1 not reachable

Translation unit #3:export module M; // primary module interface unit export import :A; void g(); // error: #2 is reachable — end example]

G.3.5[ifndr:basic.def.odr.definition.matches]
Specified in: [basic.def.odr]

If there are definitions in different translation units of a definable item D where
  • D is not defined by an injected declaration ([expr.const.reflect]),
  • D is not an inline or templated function or variable, and
  • D is not attached to a named module or the declarations are not reachable from one another,
that do not satisfy the matching rules described in [basic.def.odr], the program is ill-formed, no diagnostic required.
[Example 1: 

Translation unit #1:inline void f() {} // #1 inline void g() {} // #2 inline void h() {[]{}();} // #3 namespace { int i = 0; } inline void j() {++i;} // #4

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, i refers to different entity than in #4 — end example]

G.3.6[ifndr:basic.def.odr.unnamed.enum.same.type]
Specified in: [basic.def.odr]

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 ([dcl.enum]) that are not the same enumeration is ill-formed, no diagnostic required.
[Example 1: 

Source file "a.h":enum { a };

Source file "b.h":enum { a, b };

Source file "main.cpp":import "a.h"; import "b.h"; auto n = decltype(a)::b; // IFNDR, more than one unnamed enum definition reachable at // this point but their types are not the same — end example]

G.3.7[ifndr:basic.contract.vastart.contract.predicate]

The use of va_start ([cstdarg.syn]) within the predicate of a contract assertion is ill-formed, no diagnostic required.
[Example 1: void f(...) { va_list args; contract_assert((va_start(const_cast<decltype(args)>(args)), true)) // IFNDR } — end example]

G.3.8[ifndr:basic.contract.handler.replacing.nonreplaceable]

On platforms where the contract-violation handler is not replaceable ([dcl.fct.def.replace]) a function declaration which could be such a replacement function is ill-formed, no diagnostic required.
[Example 1: #include <contracts> void handle_contract_violation(const std::contracts::contract_violation& violation); // IFNDR, if contract-violation handler is not replaceable — end example]

G.3.9[ifndr:class.member.lookup.name.refers.diff.decl]
Specified in: [class.member.lookup]

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.
[Example 1: struct foo {}; struct bar { foo *m_foo; foo *foo() { return m_foo; } // IFNDR, foo now refers to member function foo() while previously referred to struct foo }; — end example]
[Example 2: struct B { static int f(); }; struct D : public B { using B::f; int g(decltype(f()) x) { return 0; } // IFNDR, decltype(f()) will refer to B​::​f() here but if // moved to the end of D it would refer to D​::​f() static float f(); }; int main() { D d; return d.g(0); } — end example]

G.4 [expr]: Expressions [ifndr.expr]

G.4.1[ifndr:expr.prim.req.always.sub.fail]

If the substitution of template arguments into a requirement would always result in a substitution failure, the program is ill-formed; no diagnostic required.
[Example 1: template <typename T> concept C = requires { new int[-(int)sizeof(T)]; // IFNDR, the size of the allocation is required to be greater // than zero but can never be }; — end example]

G.5 [stmt]: Statements [ifndr.stmt]

G.5.1[ifndr:stmt.ambig.bound.diff.parse]
Specified in: [stmt.ambig]

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.
[Example 1: template <int N> struct A { const static int a = 20; }; template <> struct A<100> { using a = char; }; const int x = 10; int main() { using T = const int; T(x) (100), (y)(A<x>::a); // IFNDR, during trial parse the template parameter x is bound to // the global x later during parsing the template parameter x // is bound to the local x declared on the same line } — end example]

G.6 [dcl]: Declarations [ifndr.dcl]

G.6.1[ifndr:dcl.constinit.specifier.not.reachable]
Specified in: [dcl.constinit]

If the initializing declaration of a variable without the constinit specifier has the constinit specifier applied to declarations that are not reachable from that initializing declaration, the program is ill-formed, no diagnostic required.
[Example 1: 

Translation unit #1:int x = 5; // initializing declaration of x

Translation unit #2:extern constinit int x; // IFNDR, not reachable from initializing declaration of x — end example]

G.6.2[ifndr:dcl.inline.missing.on.definition]
Specified in: [dcl.inline]

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.
[Example 1: 

Translation unit #1:inline int f();

Translation unit #2:int f() { return 17; } // IFNDR, end of definition domain but no inline declaration of f is reachable — end example]

G.6.3[ifndr:dcl.fct.default.inline.same.defaults]
Specified in: [dcl.fct.default]

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.
[Example 1: 

Translation unit #1:inline int f(int x, int y = 2); inline int f(int x = 1, int y); // IFNDR, default arguments of f are 1 and 2

Translation unit #2:inline int f(int x = 3, int y = 4); // IFNDR, default arguments of f are 3 and 4 — end example]

G.6.4[ifndr:dcl.contract.func.mismatched.contract.specifiers]
Specified in: [dcl.contract.func]

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.
[Example 1: 

Translation unit #1:int f(int x) pre(x >= 0); // IFNDR, pre present, not present in the other first declaration of f int g(int x) pre(x == 0); // IFNDR, pre differs from the other first declaration of g int h(int x) pre(x <= 0); // OK, pre equivalent to pre on the other first declaration of h

Translation unit #2:int f(int x); // IFNDR, pre not present, present in the other first declaration of f int g(int x) pre(x != 0); // IFNDR, pre differs from the other first declaration of g int h(int y) pre(y <= 0); // OK, pre equivalent to pre on the other first declaration of h — end example]

G.6.5[ifndr:dcl.fct.def.replace.bad.replacement]
Specified in: [dcl.fct.def.replace]

A declaration of a replaceable function that is inline, not attached to the global module, does not have C++ 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.
[Example 1: extern "C" // IFNDR, wrong language linkage inline // IFNDR, inline int // IFNDR, wrong return type handle_contract_violation(const std::contracts::contract_violation&) {} void* operator new(decltype(sizeof(0))) noexcept; // IFNDR, mismatched exception specification to // declaration in <new> — end example]

G.6.6[ifndr:dcl.link.mismatched.language.linkage]
Specified in: [dcl.link]

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.
[Example 1: 

Translation unit #1:extern "C" { void f(); }

Translation unit #2:extern "C++" { void f(); } // IFNDR, different language linkage — end example]

G.6.7[ifndr:dcl.align.diff.translation.units]
Specified in: [dcl.align]

No diagnostic is required if declarations of an entity have different alignment-specifiers in different translation units.
[Example 1: 

Translation unit #1:struct S { int x; } s, *p = &s;

Translation unit #2:struct alignas(16) S; // IFNDR, definition of S lacks alignment extern S* p; — end example]

G.6.8[ifndr:dcl.attr.indet.mismatched.declarations]
Specified in: [dcl.attr.indet]

If two first declarations of a function declare a function parameter with mismatched uses of the indeterminate attribute, the program is ill-formed, no diagnostic required.
[Example 1: 

Translation unit #1:int h(int x [[indeterminate]]); // IFNDR, mismatched [[indeterminate]] to other first declaration of h

Translation unit #2:int h(int x); // IFNDR, mismatched [[indeterminate]] to other first declaration of h — end example]

G.6.9[ifndr:dcl.attr.noreturn.trans.unit.mismatch]
Specified in: [dcl.attr.noreturn]

No diagnostic is required if a function is declared in one translation unit with the noreturn attribute but has declarations in other translation units without the attribute.
[Example 1: 

Translation unit #1:[[noreturn]] void f() {}

Translation unit #2:void f(); // IFNDR, declared without noreturn — end example]

G.7 [module]: Modules [ifndr.module]

G.7.1[ifndr:module.unit.reserved.identifiers]
Specified in: [module.unit]

Specifying a module-name beginning with an identifier consisting of std followed by zero or more digits, or containing a reserved identifier ([lex.token]) in a module-declaration is ill-formed, no diagnostic required.
[Example 1: module std; // IFNDR, std is not allowed at the beginning module module; // IFNDR, module is a reserved identifier module std0; // IFNDR, std followed by digits is not allowed at the beginning export module _Test; // IFNDR, _Test is a reserved identifier export module te__st; // IFNDR, te__st is a reserved identifier — end example]

G.7.2[ifndr:module.unit.named.module.no.partition]
Specified in: [module.unit]

Having multiple primary module interface units for a named module is ill-formed, no diagnostic required.
[Example 1: module A; export import :Internals; // IFNDR, module partition not allowed — end example]

G.7.3[ifndr:module.unit.unexported.module.partition]
Specified in: [module.unit]

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 ([module.import]), the program is ill-formed, no diagnostic required.
[Example 1: 

Translation unit #1:export module M; // primary module interface unit export import :A;

Translation unit #2:export module M:A; // OK, directly exported by M export import :B;

Translation unit #3:export module M:B; // OK, indirectly exported by M

Translation unit #4:export module M:C; // IFNDR, not directly or indirectly exported by M — end example]

G.7.4[ifndr:module.private.frag.other.module.units]
Specified in: [module.private.frag]

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.
[Example 1: 

Translation unit #1:export module M; module :private; // private module fragment

Translation unit #2:module M:A; // IFNDR, partition of M with private module fragment — end example]

G.8 [class]: Classes [ifndr.class]

G.8.1[ifndr:class.base.init.delegate.itself]
Specified in: [class.base.init]

If a constructor delegates to itself directly or indirectly, the program is ill-formed, no diagnostic required
[Example 1: struct C { C( int ) { } // #1: non-delegating constructor C(): C(42) { } // #2: delegates to #1 C( char c ) : C(42.0) { } // #3: IFNDR due to recursion with #4 C( double d ) : C('a') { } // #4: IFNDR due to recursion with #3 }; — end example]

G.8.2[ifndr:class.virtual.pure.or.defined]
Specified in: [class.virtual]

If a virtual function that is not pure has no definition, the program is ill-formed, no diagnostic required.
[Example 1: class A { virtual void f(); }; int main() { A a; // IFNDR, virtual function that is not pure but has no definition } — end example]

G.9 [over]: Overloading [ifndr.over]

G.9.1[ifndr:over.literal.reserved]
Specified in: [over.literal]

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.
[Example 1: float operator ""E(const char*); // IFNDR, reserved literal suffix double operator"" _Bq(long double); // IFNDR, uses the reserved identifier _Bq — end example]

G.10 [temp]: Templates [ifndr.temp]

G.10.1[ifndr:temp.pre.reach.def]
Specified in: [temp.pre]

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 ([basic.def.odr]) in which it is implicitly instantiated ([temp.inst]) and whose corresponding specialization is not explicitly instantiated ([temp.explicit]) in some translation unit is ill-formed, no diagnostic required.
[Example 1: 

Source file "a.h":template <typename T> void f();

Source file "a.cpp":#include "a.h" int main() { f<int>(); // IFNDR, function template implicitly instantiated but not reachable definition } — end example]

G.10.2[ifndr:temp.arg.template.sat.constraints]
Specified in: [temp.arg.template]

Any partial specializations ([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.
[Example 1: template<class T> struct A { int x; }; template<template<class U> class V> struct C { V<int> y; V<int*> z; }; C<A> c; // IFNDR, specialization is not reachable from point of instantiation above and it would have // been selected if it had template<class T> struct A<T*> { long x; }; — end example]

G.10.3[ifndr:temp.constr.atomic.equiv.but.not.equiv]
Specified in: [temp.constr.atomic]

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.
[Example 1: template <int N> concept Add1 = true; template <unsigned N> void f2() requires Add1<2 * N>; template <unsigned N> int f2() requires Add1<N * 2> && true; void h2() { f2<0>(); // IFNDR, requires determination of subsumption between atomic constraints // that are functionally equivalent but not equivalent } — end example]

G.10.4[ifndr:temp.constr.atomic.sat.result.diff]
Specified in: [temp.constr.atomic]

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.
[Example 1: template<class T> concept Complete = sizeof(T) == sizeof(T); struct A; static_assert(!Complete<A>); // #1 struct A {}; static_assert(Complete<A>); // IFNDR, satisfaction result differs from point #1 — end example]

G.10.5[ifndr:temp.constr.normal.invalid]
Specified in: [temp.constr.normal]

If during constraint normalization any such substitution results in an invalid type or expression, the program is ill-formed, no diagnostic is required.
[Example 1: template<typename T> concept A = T::value || true; template<typename U> concept B = A<U*>; template<typename V> concept C = B<V&>; // IFNDR, it would form the invalid type V&* // in the parameter mapping — end example]

G.10.6[ifndr:temp.spec.partial.general.partial.reachable]

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.
[Example 1: template<typename T> class X{ public: void foo(){}; }; template class X<void *>; // IFNDR, explicit instantiation and partial specialization is not reachable template<typename T> class X<T*>{ public: void baz(); }; — end example]

G.10.7[ifndr:temp.over.link.equiv.not.equiv]
Specified in: [temp.over.link]

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.
[Example 1: template <int I> struct A{}; // IFNDR, the following declarations are functionally equivalent but not equivalent template <int I> void f(A<I>, A<I+10>); template <int I> void f(A<I>, A<I+1+2+3+4>); — end example]

G.10.8[ifndr:temp.res.general.default.but.not.found]
Specified in: [temp.res.general]

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 ([temp.point]) but is not found by lookup for the specialization, the program is ill-formed, no diagnostic required.
[Example 1: void f(long); // #1 void f(int, int); // #2 template<typename T> void g(T t) { f(t); } void f(int, int = 0); // #3 void h() { g(0); } // IFNDR, selects #3 here but selects #1 using lookup for g<int> — end example]

G.10.9[ifndr:temp.point.diff.pt.diff.meaning]
Specified in: [temp.point]

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 ([basic.def.odr]), the program is ill-formed, no diagnostic required.
[Example 1: 

Source file "a.h":#include <type_traits> template <typename T, typename Enabler = void> struct is_complete : std::false_type {}; template <typename T> struct is_complete<T, std::void_t<decltype(sizeof(T) != 0)>> : std::true_type {};

Source file "a.cpp":#include "a.h" struct X; static_assert(!is_complete<X>::value); // IFNDR, different specialization selected in "b.cpp"

Source file "b.cpp":#include "a.h" struct X { }; static_assert(is_complete<X>::value); // IFNDR, different specialization selected in "a.cpp" — end example]

G.10.10[ifndr:temp.dep.candidate.different.lookup.different]
Specified in: [temp.dep.candidate]

If considering all function declarations with external linkage in the associated namespaces in all translations would make a dependent call ([temp.dep]) ill-formed or find a better match, the program is ill-formed, no diagnostic required.
[Example 1: 

Translation unit #1:namespace A { struct S {}; void f(S&, long x, int y); // #3 void g(S&, int x); // #4 }

Translation unit #2:namespace A { struct S {}; void f(S&, int x, long y); // #5 void g(S&, long x); // #6 } template <typename T> void h(T& t) { f(t, 1, 1); // Selects #5 in h<A​::​S>, would be ambiguous call if all declarations were considered. g(t, 1); // Selects #6 in h<A​::​S>, would select #4 if all declarations were considered. } — end example]

G.10.11[ifndr:temp.explicit.decl.implicit.inst]
Specified in: [temp.explicit]

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 ([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.
[Example 1: extern template class std::vector<int>; // explicit instantiation declaration int main() { std::cout << std::vector<int>().size(); // IFNDR, implicit instantiation but no explicit // instantiation definition } — end example]

G.10.12[ifndr:temp.expl.spec.unreachable.declaration]
Specified in: [temp.expl.spec]

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.
[Example 1: 

Source file "a.h":template <typename T> struct S {};

Translation unit #2:#include "a.h" template <> struct S<int> { int oops; }; // #1

Translation unit #3:#include "a.h" S<int> s; // IFNDR, #1 is not reachable but would have matched — end example]

G.10.13[ifndr:temp.expl.spec.missing.definition]
Specified in: [temp.expl.spec]

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.
[Example 1: template <typename T> int f(T&&) { return 0; } template <> int f<int>(int&&); int j = f(1); // IFNDR, odr-use of f<int> with no definition — end example]

G.10.14[ifndr:temp.deduct.general.diff.order]
Specified in: [temp.deduct.general]

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.
[Example 1: template <class T> struct A { using X = typename T::X; }; template <class T> typename T::X h(typename A<T>::X); // #1 template <class T> auto h(typename A<T>::X) -> typename T::X; // redeclaration #2 template <class T> void h(...) { } void x() { h<int>(0); // Substituting into #1 forms an invalid type from T​::​X and does // not attempt to instantiate A. // Substituting into #2 instantiates A<T>, which is ill-formed. } — end example]

G.11 [cpp]: Preprocessing directives [ifndr.cpp]

G.11.1[ifndr:cpp.cond.defined.after.macro]
Specified in: [cpp.cond]

If the expansion of a macro produces the preprocessing token defined the program is ill-formed, no diagnostic required.
[Example 1: #define A defined #if A // IFNDR, defined is generated by macro replacement in controlling expression #endif — end example]

G.11.2[ifndr:cpp.cond.defined.malformed]
Specified in: [cpp.cond]

If the defined unary operator is used when it does not match one of the specified grammatical forms, the program is ill-formed, no diagnostic required.
[Example 1: #define A #define B A) #if defined ( B // IFNDR, unary operator defined did not match valid form before replacement #endif — end example]

G.11.3[ifndr:cpp.include.malformed.headername]
Specified in: [cpp.include]

If the header-name-tokens after an include directive cannot be formed into a header-name (with implementation-defined treatment of whitespace), the program is ill-formed, no diagnostic required.
[Example 1: #include// IFNDR, does not match one of the two allowable forms — end example]