This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 118f. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2025-11-07
(From submission #680.)
A specification is missing when a both a declaration of type "array of unknown bound of T" as well as one of the type "array of N T" is reachable. We should take the latter as the type.
Proposed resolution (reviewed by CWG 2025-10-10) [SUPERSEDED]:
Change in 6.9.1 [basic.types.general] paragraph 5 as follows:
A classthat has been declared but not definedtype in certain contexts (11.4.1 [class.mem.general]), an enumeration type in certain contexts (9.8.1 [dcl.enum]), or an array of unknown bound or of incomplete element type (9.3.4.5 [dcl.array]), is an incompletely-defined object type. [ Footnote: ... ] Incompletely-defined object types and cv void are incomplete types (6.9.2 [basic.fundamental]). [Note 2: Objects cannot be defined to have an incomplete type (6.2 [basic.def]). —end note]
Remove 6.9.1 [basic.types.general] paragraph 6:
A class type (such as “class X”) can be incomplete at one point in a translation unit and complete later on; the type “class X” is the same type at both points. The declared type of an array object can be an array of incomplete class type and therefore incomplete; if the class type is completed later on in the translation unit, the array type becomes complete; the array type at those two points is the same type. The declared type of an array object can be an array of unknown bound and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types.
Change in 9.3.4.5 [dcl.array] paragraph 7 and 8 as follows:
An array type "array of N U" is complete at a program point P if U is complete at P, otherwise the array type is incomplete at P. A type "array of unknown bound of U" is incomplete.
In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted in some cases in the declaration of a function parameter (9.3.4.6 [dcl.fct]). An array bound may also be omitted when an object (but not a non-static data member) of array type is initialized and the declarator is followed by an initializer (9.5 [dcl.init], 11.4 [class.mem], 7.6.1.4 [expr.type.conv], 7.6.2.8 [expr.new]). In these cases, the array bound is calculated from the number of initial elements (say, N) supplied (9.5.2 [dcl.init.aggr]), and the type of the array is “array of N U”.
Furthermore, if there is a reachable declaration of the entity that specifies a bound and has the same host scope (6.4.1 [basic.scope.scope]), an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.
If the declared type of a variable v in a declaration D2 is "array of unknown bound of U", its type is considered to be "array of N U" at any program point from which a declaration D of v is reachable (10.7 [module.reach]), where D specifies a bound N and has the same host scope (6.4.1 [basic.scope.scope]) as D2.
CWG 2025-10-10
The last green paragraph is subsumed by 10.7 [module.reach] paragraph 5 (and thus should be turned into a suitably abstract note). However, that subclause does not specify the "same host scope" restriction, which prevents leaking information from block-scope redeclarations. To address that, it might be worthwhile to change the definition of "reachable" to ignore block-scope declarations in non-enclosing scopes. Further investigation shows that implementations surprisingly ignore global-scope declarations entirely when a block-scope declaration is in view:
int a[3];
void f() {
extern int a[];
// "a" has type "array of unknown bound" in all implementations
}
CWG 2025-11-05
After extensive discussion, CWG believes a satisfactory approach entails the following parts:
Proposed resolution (2025-11-06):
Change in 6.9.1 [basic.types.general] paragraph 5 as follows:
A classthat has been declared but not definedtype in certain contexts (11.4.1 [class.mem.general]), an enumeration type in certain contexts (9.8.1 [dcl.enum]), or an array of unknown bound or of incomplete element type (9.3.4.5 [dcl.array]), is an incompletely-defined object type. [ Footnote: ... ] Incompletely-defined object types and cv void are incomplete types (6.9.2 [basic.fundamental]). [Note 2: Objects cannot be defined to have an incomplete type (6.2 [basic.def]). —end note]
Remove 6.9.1 [basic.types.general] paragraph 6:
A class type (such as “class X”) can be incomplete at one point in a translation unit and complete later on; the type “class X” is the same type at both points. The declared type of an array object can be an array of incomplete class type and therefore incomplete; if the class type is completed later on in the translation unit, the array type becomes complete; the array type at those two points is the same type. The declared type of an array object can be an array of unknown bound and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types.
Change in 7.6.2.10 [expr.reflect] bullet 7.1 as follows:
A reflect-expression R of the form ^^id-expression represents an entity determined as follows:
- If the id-expression denotes
then R is ill-formed.
- a variable declared by an init-capture (7.5.6.3 [expr.prim.lambda.capture]),
- a function-local predefined variable (9.6.1 [dcl.fct.def.general]),
- a function or variable that was introduced by a declaration that inhabits a block scope and declares a function (9.3.4.6 [dcl.fct]) or uses the extern specifier,
- a local parameter introduced by a requires-expression (7.5.8 [expr.prim.req]), or
- a local entity E (6.1 [basic.pre]) for which a lambda scope intervenes between the point at which E was introduced and R,
- ...
Change in 9.3.4.5 [dcl.array] paragraph 7 and 8 as follows:
An array type "array of N U" is complete at a program point P if U is complete at P, otherwise the array type is incomplete at P. A type "array of unknown bound of U" is incomplete.
In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted in some cases in the declaration of a function parameter (9.3.4.6 [dcl.fct]). An array bound may also be omitted when an object (but not a non-static data member) of array type is initialized and the declarator is followed by an initializer (9.5 [dcl.init], 11.4 [class.mem], 7.6.1.4 [expr.type.conv], 7.6.2.8 [expr.new]). In these cases, the array bound is calculated from the number of initial elements (say, N) supplied (9.5.2 [dcl.init.aggr]), and the type of the array is “array of N U”.
Furthermore, if there is a reachable declaration of the entity that specifies a bound and has the same host scope (6.4.1 [basic.scope.scope]), an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.
Change in 9.13.2 [dcl.align] paragraph 6 as follows:
If the defining declaration of an entity has an alignment-specifier, any non-defining declaration of that entity shall either specify equivalent alignment or have no alignment-specifier. Conversely, if any declaration of an entity has an alignment-specifier , every defining declaration of that entity shall specify an equivalent alignment. No diagnostic is required if two declarations of an entity have different alignment-specifiersin different translation unitsand neither is reachable from the other. [Example 2: ... -- end example ]
Change in 10.7 [module.reach] paragraph 3 as follows:
A declaration D is reachable from a point P if
- P is a non-synthesized point and
- D appears prior to P in the same translation unit and, if D inhabits a block scope S and declares a function or uses the extern specifier, S encloses P, or
- D is not discarded (10.4 [module.global.frag]), appears in a translation unit that is reachable from P , and does not appear within a private-module-fragment; or
- D is the injected declaration for which P is the corresponding synthesized point.
CWG 2025-11-06
CWG seeks consent from EWG for the following changes:
extern int a[];
int g();
void f() {
extern int a[3];
[[nodiscard]] int g();
}
// "a" has type "array of unknown bound" here
void h()
{
g(); // no warning about discarding the return value
}
Furthermore, CWG seeks advice fron EWG on whether a block-scope extern declarations observes the merged properties of an entity (consistent with other non-block-scope redeclarations and, for arrays of unknown bound, with widespread current behavior of implementations in C mode), or hides the effects of earlier declarations entirely (for arrays of unknown bound, consistent with widespread current behavior of implementations in C++ mode; see previous note). Example:
int a[3];
[[nodiscard]] int g();
void f() {
extern int a[];
// "::a" should (still) complete here, but
// is "a" complete here?
int g();
g(); // warning about discarding the return value?
}
Sent to EWG via paper issue #2538.