<html>
<head>
<title>Runtime-sized arrays with automatic storage duration</title>

<style type="text/css">
  ins { text-decoration:none; font-weight:bold; background-color:#A0FFA0 }
  del { text-decoration:line-through; background-color:#FFA0A0 }
  strong { font-weight: inherit; color: #2020ff }
</style>

</head>

<body>
N3366=12-0056<br/>
Jens Maurer<br/>
2012-02-17

<h1>Runtime-sized arrays with automatic storage duration</h1>

<h2>Motivation</h2>

<p>
Sometimes, a user wishes to allocate a local array whose size is not
known at compile-time, but at runtime only.  Nonetheless, the array's
size will remain unchanged during the lifetime of the array.
</p>


<p>
Examples are
<ul>
<li>interfacing with the readv/writev POSIX system calls</li>
<li>small runtime-sized data structure to apply STL algorithms
calls</li>
<li>...</li>
</ul>
</p>

<p>
This paper proposes to add local runtime-sized arrays with automatic
storage duration to C++, for example:</p>

<pre>
void f(std::size_t n)
{
   int a[n];
   for (std::size_t i = 0; i < n; ++i)
       a[i] = 2*i;
   std::sort(a, a+n);
}
</pre>
<p>
Traditionally, the array bound "n" had to be a constant expression
(see 8.3.4 dcl.array).  For local arrays with automatic storage
duration, this paper proposes to lift that restriction.  The syntax is
intended to be the same as that used for C99 variable length arrays
(VLAs).
</p>

<p>
There is well-established existing practice with gcc, Clang, and Intel
C++ all implementing a similar, if not identical feature.  In fact,
Douglas Gregor reported in c++std-ext-12553 on 2012-01-30:
</p>
<blockquote>
Users really seem to want this feature. It's a fairly common
extension, and when we tried to ban it out of principle (in Clang),
our users reacted *very* strongly.
</blockquote>


<h2>Scope</h2>

This paper does not propose to add all features of C99 variable
length arrays to C++.  In particular, the following features are
explicitly <em>excluded</em>:

<ul>
<li>multidimensional arrays, where other than the top level has a runtime bound</li>
<li>modifications to the function declarator syntax</li>
<li>sizeof(a) being a runtime-evaluated expression returning the size
of a</li>
<li>"typedef int a[n];" evaluating "n" and passing that through the
typedef</li>
</ul>

Examples:

<pre>
void f(std::size_t n)
{
  int a[n];
  unsigned int x = sizeof(a);            // ill-formed
  const std::type_info& ti = typeid(a);  // ill-formed
  typedef int t[n];                      // ill-formed
}
</pre>


<h2>Discussion</h2>

<p>
Data structures that allocate from the heap access, by design, a
global resource that is often highly contended in a multi-threaded
program.  Therefore, avoiding heap allocations is usually advantageous
for performance. Allocating such data from the stack is much more
efficient, because the stack is local to each thread and bytes on the
stack are often cached locally.  (Since each thread has a separate
stack, it is unlikely that another thread on another CPU accesses the
same data, thereby causing more expensive cache invalidations.)
</p>

<p>
The syntax does not require additional keywords.  Instead, a
restriction on the existing array declaration syntax is lifted in
certain circumstances.
</p>

<p>
There is no reason to limit the feature to PODs as array element
types, thus such a limitation is not proposed.
</p>

<p>
Stack overflow becomes more likely, in particular if the size depends on
external input and is not properly checked.  Some environments might
therefore prohibit the use of the feature.  Such a prohibition can be
easily enforced with a static analysis tool.
</p>

<p>
There is no longer an upper bound on the size of a function's stack
frame.  This makes static analysis of stack usage harder.
</p>


<h2>Open Issues</h2>

<ul>
<li>What happens when the runtime size of an array is smaller than the
number of elements in its initializer list?  The current wording (see
below) specifies "undefined behavior", other options would be to throw
an exception or to silently increase the array size to have at least
as many elements as required by the initializer list.</li>
</ul>

<h2>Alternatives</h2>

The following alternatives were considered:

<ul>
<li>std::dynarray&lt;T> as presented in N2648 "C++ Dynamic Arrays" by
Lawrence Crowl and Matt Austern: stack allocation is left as QoI,
requiring "compiler detection" when using dynarray&lt;T> locally; also
usable with static storage duration</li>
<li>alloca(): not type-safe, no support for C++ constructors/destructors</li>
<li>std::vector: allocates from heap, allows space "reservation", thus
requiring more management data</li>
<li>std::valarray: allocates from heap; additional unrelated
requirements on contained type (see 26.2 numeric.requirements)</li>
<li>"auto_buffer" (fixed-size local buffer plus overflow to the heap):
may waste memory for fixed-size buffer; introduces additional layer of
pointer indirection</li>
</ul>


<h2>Changes to the Working Draft</h2>

Change in 3.9 basic.types paragraph 10:
<blockquote>
A type is a <em>literal type</em> if it is:
<ul>
<li>...</li>
<li>an array of literal type <ins>other than an array of runtime
bound</ins>; or
<li>...</li>
</ul>
</blockquote>

Change in 4.2 conv.array paragraph 1:

<blockquote>
An <del>lvalue or rvalue</del> <ins>expression</ins> of type "array of
N T"<ins>, "array of runtime bound of T",</ins> or "array of unknown
bound of T" can be converted to a prvalue of type "pointer to T". The
result is a pointer to the first element of the array.
</blockquote>

Insert a new paragraph before 5.2.8 expr.typeid paragraph 2:

<blockquote>
<ins>The <code>typeid</code> operator shall not be applied to an array
of runtime bound.</ins>
<p>
When <code>typeid</code> is applied to a glvalue expression ...
</blockquote>

Change in 5.3.3 expr.sizeof paragraph 1:

<blockquote>
... The <code>sizeof</code>
operator shall not be applied to an expression that has function or
incomplete type, to an enumeration type whose underlying type is not
fixed before all its enumerators have been declared,
<ins>to an array of runtime bound,</ins>
to the parenthesized name of such types, or to an lvalue that
designates a bit-field. ...
</blockquote>

Insert a new paragraph before 7.1.3 dcl.typedef paragraph 3:

<blockquote>
<ins>A <var>typedef-name</var> shall not name an array of runtime
bound.</ins>
<p>
In a given non-class scope, a <code>typedef</code> specifier can be used to
redefine the name of any type declared in that scope to refer to the
type to which it already refers. [ Example: ... ]
</blockquote>

Change in 7.1.6.2 dcl.type.simple paragraph 3:

<blockquote>
The type denoted by decltype(e) is defined as follows:
<ul>
<li><ins>if <code>e</code> has type "array of runtime bound", the
program is ill-formed;</ins></li>
<li><ins>otherwise,</ins> if <code>e</code> is an unparenthesized ...</li>
</ul>

</blockquote>


Change in 8.3.1 dcl.ptr paragraph 1:

<blockquote>
... Similarly, the optional attribute-specifier-seq (7.6.1) appertains to
the pointer and not to the object pointed to.  <ins>There shall be no
pointers to arrays of runtime bound.</ins>
</blockquote>

Change in 8.3.2 dcl.ref paragraph 5:

<blockquote>
There shall be no references to references, <ins>no references to
arrays of runtime bound</ins>, no arrays of references, and no
pointers to references. ...
</blockquote>

Change in 8.3.4 dcl.array paragraph 1:

<blockquote>
In a declaration T D where D has the form
<pre>
           D1 [ <del><var>constant-expression</var><sub>opt</sub></del> <ins><var>expression</var></ins> ] <var>attribute-specifier-seq<var><sub>opt</sub>
</pre>
and the type of the identifier in the declaration T D1 is
"<em>derived-declarator-type-list</em> T", then the type of the
identifier of D is an array type; if the type of the identifier of D
contains the <code>auto</code> <var>type-specifier</var>, the program
is ill-formed.

T is called the array element type; this type shall not be a reference
type, the (possibly cv-qualified) type <code>void</code>, a function
type, <ins>an array of unknown or runtime bound,</ins> or an abstract class type.

<ins>Except as noted below, if the <var>expression</var> is omitted,
the type of the identifier of D is
"<em>derived-declarator-type-list</em> array of unknown bound of T".
If the <em>expression</em> is present and
it is an integral constant expression (5.19 expr.const) with value
<code>N</code>, N shall be greater than zero and the type of the
identifier of D is "<em>derived-declarator-type-list</em> array of N
T".
If the <em>expression</em> is present, but not an integral constant
expression, its value is implicitly converted to a prvalue of type
<code>std::size_t</code> denoting the number of elements in the array,
and the type of the identifier of D is
"<em>derived-declarator-type-list</em> array of runtime bound of T".

An object of array type contains a contiguously allocated
non-empty set of N elements of type T, numbered 0 to N-1.

</ins> 

<del>If the <var>constant-expression
(5.19 expr.const)</var> <var>expression</var> is present, it shall be
an integral constant expression and its value shall be greater than
zero.

The constant expression specifies the bound of (number of elements in)
the array. If the value of the constant expression is N, the array has
N elements numbered 0 to N-1, and the type of the identifier of D is
"<em>derived-declarator-type-list</em> array of N T".
An object of array type contains a contiguously allocated non-empty
set of N subobjects of type T. Except as noted below, if the constant
expression is omitted, the type of the identifier of D is
"<em>derived-declarator-type-list</em> array of unknown bound of T",
<strong>an incomplete object type</strong>.</del>

The type "<em>derived-declarator-type-list</em> array of N T" is a
different type from the type "<em>derived-declarator-type-list</em>
array of unknown bound of T", see 3.9 basic.types.  Any type of the
form "<em>cv-qualifier-seq</em> array of N T" is adjusted to "array of
N <em>cv-qualifier-seq</em> T", and similarly for "array of unknown
bound of T".

The optional <em>attribute-specifier-seq</em> appertains to the
array. [ Example: 
<pre>
  typedef int A[5], AA[2][3];
  typedef const A CA;         // type is "array of 5 const int"
  typedef const AA CAA;       // type is "array of 2 array of 3 const int"

<ins>  void f(unsigned int n) {
    int a[n];     // type of "a" is "array of runtime bound of int"
  }</ins>

</pre>
-- end example ] [ Note: ... ]

</blockquote>


Change in 8.3.4 dcl.array paragraph 3:

<blockquote>
When several "array of" specifications are adjacent, a
multidimensional array is created<del>; only the first of the constant
expressions that specify the bounds of the arrays may be
omitted</del>.  In addition to ...

</blockquote>

Add a new paragraph before 8.3.4 dcl.array paragraph 4:

<blockquote>
<ins>An array of runtime bound shall only be used as the type of a
local object with automatic storage duration.</ins>
</blockquote>

Change in 8.3.5 dcl.fct paragraph 8:

<blockquote>
If the type of a parameter includes a type of the form <ins>"array of
runtime bound of T",</ins> "pointer to array of unknown bound of
T"<ins>,</ins> or "reference to array of unknown bound of T," the
program is ill-formed. [ Footnote: ... ]  Functions shall not have a
return type of type array or function, although they may have a return
type of type pointer or reference to such things.
</blockquote>

Change in 8.5.1 dcl.init.aggr paragraph 6:

<blockquote>
<ins>When initializing an array of runtime bound and the number of
<var>initializer-clause</var>s exceeds the number of elements to
initialize, the program has undefined behavior.  For other array
types, an</ins>
<del>An</del> <var>initializer-list</var> is ill-formed if the number of
<var>initializer-clause</var>s exceeds the number of members or
elements to initialize. [ Example: ... ]

</blockquote>

Change in 9.2 class.mem paragraph 10:

<blockquote>
<del>Non-static</del> <ins>A non-static</ins> (9.4 class.static) data
<del>members</del> <ins>member</ins> shall not have incomplete
<del>types.</del> <ins>type or type "array of runtime bound". [ Note:
</ins> In particular, a class C shall not contain a non-static member
of class C, but it can contain a pointer or reference to an object of
class C. <ins>]</ins>

</blockquote>


Change in 14.1 temp.param paragraph 7:
<blockquote>
A non-type <var>template-parameter</var> shall not be declared to have
floating point, class, <ins>array of runtime bound</ins>, or void
type.  [ Example: ... ] </blockquote>
