<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<TITLE>
    CWG Issue 619</TITLE>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<STYLE TYPE="text/css">
  INS { text-decoration:none; font-weight:bold; background-color:#A0FFA0 }
  .INS { text-decoration:none; background-color:#D0FFD0 }
  DEL { text-decoration:line-through; background-color:#FFA0A0 }
  .DEL { text-decoration:line-through; background-color: #FFD0D0 }
  @media (prefers-color-scheme: dark) {
    HTML { background-color:#202020; color:#f0f0f0; }
    A { color:#5bc0ff; }
    A:visited { color:#c6a8ff; }
    A:hover, a:focus { color:#afd7ff; }
    INS { background-color:#033a16; color:#aff5b4; }
    .INS { background-color: #033a16; }
    DEL { background-color:#67060c; color:#ffdcd7; }
    .DEL { background-color:#67060c; }
  }
  SPAN.cmnt { font-family:Times; font-style:italic }
</STYLE>
</HEAD>
<BODY>
<P><EM>This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21
  Core Issues List revision 118b.
  See http://www.open-std.org/jtc1/sc22/wg21/ for the official
  list.</EM></P>
<P>2025-09-28</P>
<HR>
<A NAME="619"></A><H4>619.
  
Completeness of array types
</H4>
<B>Section: </B>6.9&#160; [<A href="https://wg21.link/basic.types">basic.types</A>]
 &#160;&#160;&#160;

 <B>Status: </B>C++11
 &#160;&#160;&#160;

 <B>Submitter: </B>Steve Clamage
 &#160;&#160;&#160;

 <B>Date: </B>16 February 2007<BR>


<P>[Voted into WP at August, 2010 meeting.]</P>



<P>Is the following example well-formed?</P>

<PRE>
    struct S {
        static char a[5];
    };
    char S::a[];    // Unspecified bound in definition
</PRE>

<P>6.7 [<A href="https://wg21.link/basic.link#10">basic.link</A>] paragraph 10 certainly makes allowance
for declarations to differ in the presence or absence of a major array
bound.  However, 6.2 [<A href="https://wg21.link/basic.def#5">basic.def</A>] paragraph 5 says that</P>

<BLOCKQUOTE>

A program is ill-formed if the definition of any object gives the
object an incomplete type (6.9 [<A href="https://wg21.link/basic.types">basic.types</A>]).

</BLOCKQUOTE>

<P>6.9 [<A href="https://wg21.link/basic.types#7">basic.types</A>] paragraph 7 says,</P>

<BLOCKQUOTE>

The declared type of an array object might be an array of unknown size
and therefore be incomplete at one point in a translation unit and
complete later on; the array types at those two points (&#8220;array
of unknown bound of <TT>T</TT>&#8221; and &#8220;array of
N <TT>T</TT>&#8221;) are different types.

</BLOCKQUOTE>

<P>This wording appears to make no allowance for the C concept of
&#8220;composite type;&#8221; instead, each declaration is said
to have its own type.  By this interpretation, the example is
ill-formed, because the type declared by the definition of
<TT>S::a</TT> is incomplete.</P>

<P>If the example is intended to be well-formed, the Standard
needs explicit wording stating that an omitted array bound in a
declaration is implicitly taken from that of a visible
declaration of that object, if any.</P>

<P><B>Notes from the April, 2007 meeting:</B></P>

<P>The CWG agreed that this usage should be permitted.</P>

<P><B>Proposed resolution (June, 2008):</B></P>

<OL>
<LI><P>Change 9.3.4.5 [<A href="https://wg21.link/dcl.array#1">dcl.array</A>] paragraph 1 as follows:</P></LI>

<BLOCKQUOTE>

...<DEL>If</DEL> <INS>Except as noted below, if</INS> the constant expression
is omitted, the type of the identifier of <TT>D</TT> is
&#8220;<I>derived-declarator-type-list</I> array of unknown bound of
<TT>T</TT>,&#8221; an incomplete object type...

</BLOCKQUOTE>

<LI><P>Change 9.3.4.5 [<A href="https://wg21.link/dcl.array#3">dcl.array</A>] paragraph 3 as follows:</P></LI>

<BLOCKQUOTE>

When several &#8220;array of&#8221; specifications are adjacent, a
multidimensional array is created; <INS>only the first of</INS> the
constant expressions that specify the bounds of the arrays <DEL>can</DEL>
<INS>may</INS> be omitted <DEL>only for the first member of the
sequence. [<I>Note:</I> this elision is useful for function parameters
of array types, and when the array is external and the definition,
which allocates storage, is given elsewhere. &#8212;<I>end
note</I>]</DEL> <INS>In addition to declarations in which an incomplete
object type is allowed, an array bound may be omitted in the
declaration of a function parameter (9.3.4.6 [<A href="https://wg21.link/dcl.fct">dcl.fct</A>]).</INS>
<DEL>The first <I>constant-expression</I> can</DEL> <INS>An array bound
may</INS> also be omitted when the declarator is followed by an
<I>initializer</I> (9.5 [<A href="https://wg21.link/dcl.init">dcl.init</A>]). In this case the bound
is calculated from the number of initial elements (say, <TT>N</TT>)
supplied (9.5.2 [<A href="https://wg21.link/dcl.init.aggr">dcl.init.aggr</A>]), and the type of the identifier
of <TT>D</TT> is &#8220;array of <TT>N</TT> <TT>T</TT>.&#8221;
<INS>Furthermore, if there is a visible declaration of the name declared
by the <I>declarator-id</I> (if any) in which the bound was specified,
an omitted array bound is taken to be the same as in that earlier
declaration.</INS>

</BLOCKQUOTE>

</OL>

<P><B>Notes from the September, 2008 meeting:</B></P>

<P>The proposed resolution does not capture the result favored
by the CWG: array bound information should be accumulated across
declarations within the same scope, but a block extern declaration
in a nested scope should not inherit array bound information from
the outer declaration.  (This is consistent with the treatment of
default arguments in function declarations.)  For example:</P>

<PRE>
    int a[5];
    void f() {
        extern int a[];
        sizeof(a);
    }
</PRE>

<P>Although there was some confusion about the C99 wording dealing
with this case, it is probably well-formed in C99.  However, it
should be ill-formed in C++, because we want to avoid the
concept of &#8220;compatible types&#8221; as it exists in C.</P>

<P><B>Proposed resolution (March, 2010):</B></P>

<OL>
<LI><P>Change 9.3.4.5 [<A href="https://wg21.link/dcl.array#1">dcl.array</A>] paragraph 1 as follows:</P></LI>

<BLOCKQUOTE>

...<DEL>If</DEL> <INS>Except as noted below, if</INS> the constant expression
is omitted, the type of the identifier of <TT>D</TT> is
&#8220;<I>derived-declarator-type-list</I> array of unknown bound of
<TT>T</TT>,&#8221; an incomplete object type...

</BLOCKQUOTE>

<LI><P>Change 9.3.4.5 [<A href="https://wg21.link/dcl.array">dcl.array</A>] paragraphs 3-4 as follows:</P></LI>

<BLOCKQUOTE>

<P>When several &#8220;array of&#8221; specifications are
adjacent, a multidimensional array is created; <INS>only the
first of</INS> the constant expressions that specify the bounds
of the arrays <DEL>can</DEL> <INS>may</INS> be omitted <DEL>only
for the first member of the sequence. [<I>Note:</I> this elision
is useful for function parameters of array types, and when the
array is external and the definition, which allocates storage, is
given elsewhere. &#8212;<I>end note</I>]</DEL> <INS>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 [<A href="https://wg21.link/dcl.fct">dcl.fct</A>]).</INS> <DEL>The
first <I>constant-expression</I> can</DEL> <INS>An array bound
may</INS> also be omitted when the declarator is followed by an
<I>initializer</I> (9.5 [<A href="https://wg21.link/dcl.init">dcl.init</A>]). In this case the
bound is calculated from the number of initial elements (say,
<TT>N</TT>) supplied (9.5.2 [<A href="https://wg21.link/dcl.init.aggr">dcl.init.aggr</A>]), and the type
of the identifier of <TT>D</TT> is &#8220;array of <TT>N</TT>
<TT>T</TT>.&#8221; <INS>Furthermore, if there is a preceding
declaration of the entity in the same scope in which the bound
was specified, 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.</INS>
</P>

<P>[<I>Example:...</I>
</P>

<P>...can reasonably appear in an expression.  <INS>Finally,</INS>
</P>

<PRE>
<INS>  extern int x[10];
  struct S {
    static int y[10];
  };

  int x[];                //<SPAN CLASS="cmnt">OK: bound is 10</SPAN>
  int S::y[];             //<SPAN CLASS="cmnt">OK: bound is 10</SPAN>

  void f() {
    extern int x[];
    int i = sizeof(x);    //<SPAN CLASS="cmnt">error: incomplete object type</SPAN>
  }</INS>
</PRE>

<P>&#8212;<I>end example</I>]</P>

</BLOCKQUOTE>

</OL>

<BR><BR>
</BODY>
</HTML>
