-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Reference (section label): [temp.inst]
Link to reflector thread (if any): https://lists.isocpp.org/core/2025/04/17803.php
Issue description:
When a variable has an incomplete array type and a braced initializer, the complete array type can be determined by instantiating a definition of the variable. [temp.inst]/4 and /7 say when this happens:
/4: "Unless a member of a templated class is a declared specialization, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist or if the existence of the definition of the member affects the semantics of the program; in particular, the initialization (and any associated side effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist."
/7: "Unless a variable template specialization is a declared specialization, the variable template specialization is implicitly instantiated when it is referenced in a context that requires a variable definition to exist or if the existence of the definition affects the semantics of the program. A default template argument for a variable template is implicitly instantiated when the variable template is referenced in a context that requires the value of the default argument."
Moreover, [temp.inst]/11 says:
"An implementation shall not implicitly instantiate [...] a variable template [...] or static data member of a templated class [...] unless such instantiation is required."
But it's not entirely clear what this means. Under what circumstances does "the existence of the definition of the member affect[...] the semantics of the program"?
Clang tries to determine this precisely, by checking for places where the array bound matters and only instantiating in those cases, but it sometimes gets it wrong. Other implementations seemingly instantiate a definition whenever the variable is mentioned, even in cases where the semantics of the program are unaffected by the completeness of the type of the variable. Neither approach seems entirely in line with the wording.
Some examples of divergence:
// Case 1: Clang rejects because the type of `*p` changes
// between redeclarations; Clang seems to be incorrect:
// The array bound affects the semantics of the program.
template<typename T> struct X {
static inline int arr[] = {1, 2, 3};
};
// Clang does not instantiate a definition here.
extern decltype(X<int>::arr) *p;
// Clang does instantiate a definition here.
int n = sizeof(X<int>::arr);
decltype(X<int>::arr) *p;
// Case 2: GCC, EDG, MSVC reject because they instantiate a definition
// of `X<int>::arr` even though it's not odr-used and doesn't appear to
// affect the semantics of the program.
template<typename T> struct X {
static inline int arr[] = {1, 2, T::error};
};
// The array bound here doesn't matter; array-to-pointer decay doesn't
// care about it.
decltype(+X<int>::arr) r;What is the intent?
Suggested resolution:
The rule used by non-Clang implementations seems a lot easier to implement. Assuming we want that, perhaps we should have an explicit rule in [temp.inst] similar to /8, eg something like:
"The existence of a definition of a variable is considered to affect the semantics of the program if it affects an array bound in the type of an id-expression ([dcl.array]), even if the semantics of the context in which the id-expression appears do not depend on the array bound."