Variable Length Array Proposal

Tom MacDonald uunet!tamarack.cray.com!tam
Tue Feb 2 20:59:21 PST 1993


Below is a cover letter and a proposal for a Varaible Length Array
type that X3J11.1 (Numerical C Extensions Group) is currently
debating.  If you are interested in receiving a formatted version
on paper, please send me Email and I'll mail you one.  Previously
paper copies of proposals were sent to these organizations.  I'm
trying this Email method to see how effective it is.  Thanks.

Tom MacDonald (tamacray.com)

============= Cover Letter ========== Cover Letter ==============


To:     Members of X3J11, WG14, X3J16, WG21, and X3H5

From:   X3J11.1

Topic:  Comments on Variable Length Array (VLA) Proposal  by
        the VLA Subgroup

Date:   January 22, 1993



The X3J11.1 committee is  chartered  to  explore  additional
features  that will make C a better language for numeric and
scientific programming.  We are not  producing  a  standard,
but  rather a technical report that can be used by X3J11 and
WG14 as guidance for a future standard, and by  vendors  who
wish  to  keep  pace with the state of the art in C language
development.  The addition of a Variable Length Array  (VLA)
type  to  C  was identified by X3J11.1 as being an important
issue for numeric and scientific programmers.

At a recent X3J11.1  meeting  (7-8  December  1992),  voting
members  recommended that two competing proposals be sent to
a wider audience for comments.  X3J11.1 hopes to  get  addi-
tional  input,  personal  preferences,  and further guidance
from other interested groups on both proposals.  The vote to
send  out these proposals was: 10 Yes, 1 Yes with comment, 2
No, and 2 Undecided.

This proposal is one  of  the  competing  proposals  and  is
titled,  ``Arrays  of Variable Length.''  The other proposal
is being distributed separately.  Please send your  comments
to  the  author, Tom MacDonald, whose name and Email address
appear on the proposal.  Response by Email is preferred.  We
need to receive your comments by April 1, 1993.  All of your
comments on this proposal will be collated, and the  results
will appear in a future revision of the proposal.

There is an existing implementation by Cray  Research,  Inc.
of  this proposal.  Please feel free to comment on all parts
of the proposal.  Specifically, we would appreciate  receiv-
ing comments about the following key points:

   -  The typing system of C has been enhanced to permit the
    declaration of arrays whose size is determined at execu-
    tion time.  These array  types  are  called  VLA  types.
    Only  identifiers  with  automatic  storage duration can
    have a type that  contains  a  VLA  type.   Furthermore,
    structure  and  union members cannot have VLA types.  Is
    this an acceptable limitation?

   -  For function prototypes, this proposal allows an iden-
    tifier that is declared as a parameter and appears in an
    array size expression to have its scope extended to  the
    beginning of the parameter-type-list (rather than begin-
    ning just after the completion of its  declarator).   An
    example is:

         void func(int a[n][n], int n) { ... }

    in which the parameter n is used to specify the size  of
    the  VLA  type.   This is intended to allow the use of a
    prototype in a definition and avoid forcing the  use  of
    an ``old-style'' definition such as:

         void func(a,n)
            int n;
            int a[n][n];
         { ... }

    to accomplish the same ordering.   Is  this  fundamental
    change  to  C's  lexical  ordering  within  a  prototype
    acceptable?

   -  When the operand of a sizeof operator has a VLA  type,
    the operation cannot be used anywhere a constant expres-
    sion is required.  A sizeof operator can still  be  used
    in  constant  expressions if its operand does not have a
    VLA type.  Is this addition to the semantics  of  sizeof
    acceptable?


Thank you for your time and effort.

========= Delete this line and everything above before printing ========










                  Arrays of Variable Length
                        (Revision 9)

                       X3J11.1/93-007


                       Tom MacDonald

                    Cray Research, Inc.
                    655F Lone Oak Drive
                      Eagan, MN  55121
                        tamacray.com

                      January 22, 1993


                          ABSTRACT

          The inability to declare arrays whose size is
     known  only at runtime is often cited as a primary
     deterrent to using  C  as  a  numerical  computing
     language.   Arrays  of this nature are implemented
     in the current GNU-C  and  Cray  Research  C  com-
     pilers.   The  adoption  of  GNU-C's  treatment of
     variable length arrays was proposed to  the  X3J11
     Committee  that defined the ANSI C and ISO C stan-
     dards, but was dismissed as having too  many  far-
     reaching  implications.  Eventual adoption of some
     standard notion of runtime  arrays  is  considered
     crucial  for  C's  acceptance as a major player in
     the  numerical  computing   world.    This   paper
     describes  an  implementation  of  variable length
     arrays which Cray  Research  has  chosen  for  its
     Standard  C  Compilers (SCC), with the intent that
     the more prior art in this area, the  more  likely
     it will appear in a future C standard.



                          Introduction


     This is Cray Research's proposal  for  Variable  Length
Array  (VLA)  types.   Insertion of this new array type into
the existing Standard for C requires an examination of  each
section  of the existing Standard with necessary elaboration
for those sections which appear  to  be  impacted.   Accord-
ingly,  the  numbering, titles and general format of each of
the following several paragraphs match those  found  in  the
current  C  Standard.   The  ISO  section numbers are listed
first, followed by the corresponding ANSI section numbers in



                           - 1 -





1/22/93         Arrays of Variable Length    X3J11.1/93-007


parentheses.   If a paragraph is meant to address definitive
issues relating to variable length arrays, then an  overview
of  these  issues appears followed by a brief rationale sec-
tion detailing Cray Research's current approach.


6.1.2.4 (3.1.2.4) Storage Duration of Objects


     An object whose identifier is declared with no  linkage
and without the storage-class specifier static has automatic
storage duration.  Storage is guaranteed to be reserved  for
a  new  instance of such an object on each normal entry into
the block with which it is associated.  If  the  block  with
which  the  object  is  associated is entered by a jump from
outside the block to a labeled statement in the block or  in
an enclosed block, then storage is guaranteed to be reserved
provided the object does not have a  variable  length  array
type.   If the object is variably qualified and the block is
entered by a jump to a labeled statement, then the  behavior
is  undefined.   If  an  initialization is specified for the
value stored in the object, it is performed on  each  normal
entry,  but  not  if  the  block  is  entered by a jump to a
labeled statement.  Storage for  the  object  is  no  longer
guaranteed  to  be reserved when execution of the block ends
in any way.  (Entering an enclosed block suspends  but  does
not  end  execution of the enclosing block.  Calling a func-
tion suspends but does not end execution of the  block  con-
taining  the call.)  The value of a pointer that referred to
an object with automatic storage duration that is no  longer
guaranteed to be reserved is indeterminate.

Forward reference:  variably qualified (6.5), (3.5),  vari-
able length array (6.5.4.2), (3.5.4.2).


6.3.3.4 (3.3.3.4) The sizeof operator

Semantics

     When applied to an operand that  has  array  type,  the
result is the total number of bytes in the array.  For vari-
able length array types the result is not a constant expres-
sion and is computed at program execution time.

Forward  reference:   variable   length   array   (6.5.4.2),
(3.5.4.2).










                           - 2 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007



Example 1

  int i = 2, j = 3, k, koo;
  int *p = &i;

  int func(int n) {
       char b[*p==n ? k = j++ : n+j]; /* side effects OK    */
       return (sizeof(b));            /* runtime expression */
  }

  main() {
     koo = func(10);  /* runtime sizeof; koo == 13   */
  }


ISSUE

Overview:

     The sizeof operator could previously  be  used  in  any
constant expression (except those associated with preproces-
sor directives such as #if).  With  this  extension  to  the
language, such is no longer the case since runtime code must
be generated to calculate the  size  of  a  variable  length
array type.

Rationale:

     The notion of ``size'' is an  important  part  of  such
operations  as  pointer increment, subscripting, and pointer
difference.  Although the sizeof operator will now produce a
value  computed at runtime, there still exists a consistency
when applied to the previously mentioned operations.  Furth-
ermore,  it  can  still  be used as an operand to the malloc
function and to compute the number of elements in an array.


6.4 (3.4) Constant Expressions

Semantics

     An integral constant  expression  shall  have  integral
type  and  shall  only  have  operands that are integer con-
stants, enumeration constants, character  constants,  sizeof
expressions  whose  operand  does not have a variable length
array type, and floating constants that  are  the  immediate
operands  of casts.  An arithmetic constant expression shall
have arithmetic type and shall only have operands  that  are
integer  constants,  floating  constants,  enumeration  con-
stants, character constants, and  sizeof  expressions  whose
operand does not have a variable length array type.





                           - 3 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


6.5 (3.5) Declarations

Semantic

     If the sequence of specifiers in a declarator  contains
a  variable  length  array  type,  the type specified by the
declarator is said to be variably qualified.

Forward  reference:   variable   length   array   (6.5.4.2),
(3.5.4.2).


6.5.2 (3.5.2) Type Specifiers

Contraint

     Only identifiers with block scope can have  a  variably
qualified  type.  Objects with static storage duration shall
not be declared with a variably qualified type.  Only  ordi-
nary  identifiers  (as  defined in 6.1.2.3 (3.1.2.3)) may be
declared with a variable length array type.

Forward  reference:   variable   length   array   (6.5.4.2),
(3.5.4.2).

ISSUES

Overview:

     This section discusses two  issues  about  declarations
containing  variable  length  array specifiers (i.e., VLAs).
First, VLAs must be declared at either block scope or  func-
tion  prototype scope, and must have automatic storage dura-
tion (i.e., no static storage duration objects).  This means
that  file  scope  variables and variables declared with the
static storage class specifier, can never be variably quali-
fied objects of any fashion.  Second, if the identifier that
is being declared has  a  variable  length  array  type  (as
opposed  to a pointer to an array), then it must be an ordi-
nary identifier.

















                           - 4 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007



Example 2

  extern int n;
  struct { int (*z)[n]; };       /* Error - file scope member pointer to VLA  */
  int A[n];                      /* Error - file scope VLA                    */
  static int (*p)[n];            /* Error - file scope pointer to VLA         */
  int B[100];

  void func(int m, int C[m][m]) {/* OK - prototype VLA                     */
     struct { int (*y)[n];       /* OK - block scope member pointer to VLA */
              int z[n];   };     /* Error - z is not an ordinary identifier*/
     int D[m];                   /* OK - auto VLA                         */
     static int E[m];            /* Error - static block scope VLA        */
     int (*s)[m];                /* OK - auto pointer to VLA              */
     extern int (*r)[m];         /* Error - pointer to VLA with linkage   */
     static int (*q)[m] = &B;    /* Error - static pointer to VLA         */
  }

Rationale:

     Restricting variable length array declarators to  iden-
tifiers  with  automatic  storage  duration is natural since
``variableness'' at  file  scope  requires  some  notion  of
parameterized  typing.   Previous  versions of this proposal
permitted block scope static pointers to  VLAs.   They  have
been  removed  from this version because there appears to be
no utility for them.  If a use is identified, they  are  not
difficult to add back into a later proposal.


6.5.2.1 (3.5.2.1) Structure and Union Specifiers

Contraint

     A structure or union shall not contain a member with  a
variable length array type.

Forward  reference:   variable   length   array   (6.5.4.2),
(3.5.4.2).

ISSUES

Overview:

     This issue  involves  the  declaration  of  members  of
structures  or  unions  with  variably  qualified types.  To
allow this engenders a host of problems  such  as  treatment
when passing such objects (or even pointers to such objects)
as  parameters.   In  addition,  the  offsetof  macro  would
require extended meaning, to wit:






                           - 5 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007



Example 3

  int n = 8;
  main() {
    struct tag {
       int m1;
       int m2[n];     /* not allowed by this proposal */
       int m3;
    };
    int i;
    i = offsetof(struct tag, m1); /* OK */
    i = offsetof(struct tag, m2); /* OK */
    i = offsetof(struct tag, m3); /* undefined behavior? */
  }

Since the expansion of offsetof is implementation  specific,
it  seems  impractical to guarantee any behavior for members
which might follow a  variable  length  array  type  member.
Finally, any structure or union containing a variable length
array type must not be declared at file  scope  due  to  its
variable  nature.   This forces every user to re-specify the
structure type inside each function, and raises type  compa-
tibility  questions.  Allowing formal arguments to be struc-
tures with variable length array type members  (as  well  as
pointers to same) was problematic and, since functions could
not return them either, the utility of  the  whole  exercise
centered  around  stack  allocated  structures with variable
length members.  Objects of this sort are  narrowly  focused
and  thus  the  decision  was  made to disallow the variable
length array type within structures and unions.

     If all ordinary identifiers found in the arbitrary size
expressions  of  a given member were compelled to resolve to
other members of the same  structure  or  union,  then  some
utility  could be found in allowing structures and unions to
have variably qualified members.  Such members would  become
self-contained  ``fat  pointers''  of  sorts.  The following
example demonstrates this concept:

Example 4

  struct tag {
     int n;         /* initialized to 5                  */
     int m2[n];     /* uses member `n' to complete size  */
  } x = { 5 };

The possibility of resolving the  size  of  variable  length
array  members  amongst  other  members  could  be explored.
There were two votes taken at NCEG meeting  #5  in  Norwood,
MA, which asked the following questions:

   Q:  Should there be some way to declare a VLA member
     Yes:  11    No:  1    Undecided: 4



                           - 6 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


   Q:  In favor of type shown in Example 3?   3
   Q:  In favor of type shown in Example 4?   6
   Q:  Undecided?   7

There is considerable sentiment for adding VLA  members  but
it  appears  the  implications  of each approach needs to be
explored before the committee  can  reach  consensus.   This
proposal  has  elected  to pursue VLAs outside of structures
and unions.  A future proposal may attempt  to  resolve  the
VLA issue for members.


6.5.4 (3.5.4) Declarators

6.5.4.2 (3.5.4.2) Array declarators

Constraints

     The [ and ] shall delimit an expression or *.  If [ and
]  delimit  an  expression  (which  specifies the size of an
array), it shall be an integral type.  If the expression  is
a  constant  expression  then  it shall have a value greater
than zero.

Semantic

     If, in the declaration ``T D1,'' D1 has the form

   D[assignment-expression "opt"]

or

   D[*]

and the type specified for ident in the declaration ``T  D''
is  ``derived-declarator-type-list T,'' then the type speci-
fied for ident in ``T D'' is  ``derived-declarator-type-list
array  of  T.''   If  the size expression is not present the
array type is an incomplete type.  If * is used instead of a
size  expression,  the array type is a variable length array
type of unspecified size, which can only be used in declara-
tions with function prototype scope.  If the size expression
is a constant expression and the element type  has  a  fixed
size,  the  array type is a fixed length array type.  Other-
wise, the array type is a variable length  array  type.   If
the  size  expression  is  not  a constant expression, it is
evaluated at program execution time,  it  may  contain  side
effects, and shall evaluate to a value greater than zero.

     For two arrays types to be compatible, both shall  have
compatible  element  types,  and if both size specifiers are
present and constant, then both size specifiers  shall  have
the  same constant value.  If either size specifier is vari-
able, the two sizes  must  evaluate,  at  program  execution



                           - 7 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


time,  to equal values.  It is undefined behavior if the two
size specifiers evaluate  to  unequal  values  at  execution
time.

Example 5

  extern int n;
  extern int m;

  main() {
    int a[n][6][m];
    int (*p)[4][n+1];
    int c[n][n][6][m];
    int (*r)[n][n][n+1];
    p = a;     /* error - not compatible because 4 != 6        */
    r = c;     /* compatible - assume n == 6 at execution time */
               /*              assume m==n+1 at execution time */
  }


Example 6

  int dim4 = 4;
  int dim6 = 6;

  void func() {
     int (*q)[dim4][8][dim6];
     int (*r)[dim6][8][1];
     r = q;   /* compatible, but undefined behavior at runtime */
  }

ISSUES

Overview:

     An issue raised by this section concerns type  compati-
bility  and  the  fact that variably qualified types are not
fully specified until execution time.  Is that when compati-
bility checks should occur?  Should the checks be mandatory?
Finally, the  presence  of  possible  side-effects  in  type
declarations  is  new.   Since side effects include volatile
variables there seems to be no compelling reason  to  forbid
side effects in variable length array size specifiers.

Rationale:

     The language chosen by Cray Research reflects a  ``path
of  least  resistance''  approach.  The `undefined behavior'
paradigm is invoked if things do not match up at runtime  as
they  normally  would  for  fixed  length arrays.  Prototype
side-effects are handled with a syntactic option (see use of
[*]  in  section  6.5.4.3 (3.5.4.3) below) and by inhibiting
expression evaluation until function definition time.




                           - 8 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


6.5.4.3 (3.5.4.3) Function Declarators (Including Prototypes)

Semantics

     For each parameter declared with variable length  array
type, the type used for compatibility comparisons is the one
that results from conversion to a pointer type, as in  6.7.1
(3.7.1).   In  a  function  prototype declarator that is not
part of a function definition, the syntax [*] may be ued to
pecify  a variable length array type.  In a parameter-type-
list, if an identifier is both declared as a  parameter  and
appears in  an array size expression that is not a constant
expression, then the scope of that identifier is extended to
the beginning of the parameter-type-list (rather than begin-
ning just after the completion of its declarator).   If  the
size  of  a  variable  length array depends upon the size of
itself, or another variable length  array  declared  in  the
same parameter-type-list, then the behavior is undefined.

Example 7

  /* Following prototype has a variably qualified parameter */

  void addscalar(int n, int m, double a[n][n*m + 300], double x);

  double A[4][308];

  main() {
     addscalar(4, 2, A, 3.14);
  }

  void addscalar(int n, int m, double a[n][n*m + 300], double x) {

     int i,j,k=n*m+300;

     for (i=0; i<n; i++)
        for (j=0; j<k; j++)
           a[i][j] += x;    /* `a' is pointer to a VLA
                               of size:  n*m + 300   */
  }


Example 8

 /* The following are compatible function prototype declarators: */

 void matrix_mult(int n, int m, double a[n][m], double b[m][n], double c[n][n]);

 void matrix_mult(int n, int m, double a[*][*], double b[*][*], double c[*][*]);

 void matrix_mult(int n, int m, double a[ ][*], double b[ ][*], double c[ ][*]);

 void matrix_mult(int n, int m, double a[ ][m], double b[ ][n], double c[ ][n]);




                           - 9 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


Since the order in which parameters are specified  is  unim-
portant,  the  following  compatible function prototypes are
functionally equivalent to the above four prototypes in that
the  value  of  parameters n and m are used to complete the
variable length array modifiers.

 void matrix_mult(double a[n][m], double b[m][n], double c[n][n], int n, int m);

 void matrix_mult(double a[*][*], double b[*][*], double c[*][*], int n, int m);

 void matrix_mult(double a[ ][*], double b[ ][*], double c[ ][*], int n, int m);

 void matrix_mult(double a[ ][m], double b[ ][n], double c[ ][n], int n, int m);

The following atypical program shows  that  the  meaning  of
existing  programs is preserved.  The enumeration constant n
is used in the array size expression because the size  is  a
constant expression.

Example 9

  enum { n = 5 };

  /* enum constant `n' used instead of parameter `n' */
  void f1(int a[][n], int n) {}

The following atypical program  is  not  currently  standard
conforming because the parameter x declared in function func
is a VLA.  In this case the scope of parameter t is extended
to the beginning of the parameter list.

Example 10

  enum {r=3, s=4, t=5};
  float a[6][6];

  void func(int, float [][*], int);

  main() {
     func(4, a, 2);  /* need ptr to 6 element array */
  }

  /* parameter `t' used instead of enum constant `t' */
  void func(int n, float x[][n+t], int t) {
     printf("%s\n",
        sizeof(*x)==sizeof(float [6]) ? "OK" : "Compiler error");
     return;
  }

The following function declarator contains an error  because
the  array  sizes of parameters p and q depend upon the size
of a VLA declared within the parameter-type-list.





                           - 10 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007



Example 11

  /* error - p and q use VLA sizes of parameters */

  int func(int (*p)[sizeof(*q)], int (*q)[sizeof(*p)]) { ... }

In the following function prototypes,  the  parameter  n  is
used  to complete the variable length array types.  The file
scope variable n is never referenced.

Example 12

  extern int n;         /* file scope variable is never referenced!! */

  void f(double a[][n], int n) { /* ... */ }

  extern int m;         /* file scope variable                */

  void g(double b[][n*m], int n) { /* ... */ }

The following example is also acceptable.  The  parameter  n
is used to complete the VLA size expression.

Example 13

  extern int n;

  void g(int n, int a[][n]) {  /* OK */

     /* ... */

  }

ISSUES

Overview:

     By far the most controversial issue involves the ``lex-
ical  ordering  problem''  that is presented when prototypes
with variably qualified parameters are  used  and  the  size
expression  involves  an  identifier  that  is  not visible.
Currently, programmers do not  need  to  concern  themselves
with the order in which formal parameters are specified, and
one common programming style is to declare the  most  impor-
tant  parameters  first.   Consider  the following prototype
definition:










                           - 11 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007



Example 14

  /* prototype declaration for old-style definition */

  void f(double a[*][*], int n);

  void f(a, n)
     int n;
     double a[n][n];
  {
     /* ... */
  }

The order in which the names are specified in the  parameter
list  does not depend on the order of the parameter declara-
tions themselves.  The accompanying prototype declaration is
compatible  with this definition and thus it seems appropri-
ate to allow the following prototype definition in  lieu  of
the previous old-style definition.

Example 15

  void f(double a[n][n], int n) {
     /* ... */
  }

With current lexical ordering rules  the  declaration  of  a
would  force  n  to  be  undefined or captured by an outside
declaration; but in no case would the parameter n be used as
the  programmer  clearly  intends.   This  is  an unforeseen
side-effect of Standard C prototype syntax.   The  following
vote that was taken at NCEG meeting #5 in Norwood, MA, about
the importance of preserving lexical ordering.

   In favor of preservation of lexical ordering?   4
   In favor of more relaxed rules?   4
   Undecided?   7

At X3J11.1 meeting #1 in Cupertino, CA, a proposal was  made
to  require  a permutation such that all identifiers in size
expressions must associate with a  visible  definition,  and
that  no  two permutations result in different associations.
This same vote was taken again:

   In favor of preservation of lexical ordering?   7
   In favor of more relaxed ordering?   7
   Undecided?   2

This approach introduced its own set of issues and  did  not
significantly affect the vote.

     Another issue concerns function  prototype  declarators
whose  parameters  have  variable length array types.  Since



                           - 12 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


the dimension size specifiers of variable length array types
in  prototype  declarators  which  are  not  definitions are
resolved, two passes are required over  the  parameter  type
list in order to diagnose undeclared identifiers.

  /* prototype declarator whose parameter type remains incomplete */
  /* ERROR - `x' and `y' are never declared */

  void f1(double a[x][y]);


Rationale:

     By assigning all identifiers in VLA size expressions  a
universal type that is compatible with any type, the parsing
may proceed to the matching ] token.  A possible solution to
the  ``lexical  ordering problem'' then involves tokenizing,
syntactically analyzing,  and  marking  where  the  variably
qualified expressions are found within dimension specifiers,
until the ) that  terminates  the  prototype  is  found.   A
second  scan  of  the  these  variably qualified expressions
associates  identifiers  with  universal  types  to  visible
declarations  thereby  completing  the VLA types.  If during
the second scan it is determined that an identifier used  in
a  VLA  size  expression  is still not visible, a diagnostic
message is produced.

Another issue concerns prototype  declarators  that  contain
parameters  with  variable length array types that are never
completed.  A previous version of this document allowed this
behavior.   The  motivation was to not force the implementa-
tion to make two passes over the  prototype  just  to  issue
diagnostics.   A  vote  that was taken at NCEG meeting #5 in
Norwood, MA, asked the following question:

   Q:  Which notation is preferred to denote a VLA parameter
   (in a prototype)?

   Arbitrary [x] (where x can include undeclared names) : 0
   [*] : 11
   Undecided : 3

Since the [*] syntax seems to be the  most  widely  accepted
this  proposal  has  been changed to reflect this sentiment.
Also, the presence of these diagnostics  will  help  uncover
more programmer errors.


6.5.6 (3.5.6) Type definitions

Constraints

     Typedef declarations which specify a variably qualified
type  shall  have  block scope.  The array size specified by



                           - 13 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


the variable length array type shall  be  evaluated  at  the
time  the type definition is declared and not at the time it
is used as a type specifier in an actual declarator.

Example 16

  main() {
    extern void func();
    func(20);
  }

  void func(int n) {
    typedef int A[n];   /* OK, because declared in block scope */
    A a;
    A *p;
    p = &a;
  }


Example 17

  int n;
  typedef int A[n];      /* wrong, because declared at file scope  */


Example 18

  void func(int n) {
       typedef int A[n]; /* `A' is `n' ints with `n' evaluated now */
       n += 1;
       {
          A a;           /* `a' is `n' ints - `n' without += 1 */
          int b[n];      /* `a' and `b' are different sizes    */
          for (i = 1; i < n; i++)
             a[i-1] = b[i];
       }
    }

ISSUE

Overview:

     The question arises whether the non-constant expression
which  engenders  the  variable  length array type should be
evaluated  at  the  time  the  type  definition  itself  was
declared  or  each  time  the type definition is invoked for
some object declaration.

Rationale:

     If the evaluation were to  take  place  each  time  the
typedef  name  is  used, then a single type definition could
yield variable length array types involving  many  different
dimension  sizes.   This  possibility  seemed to violate the



                           - 14 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


spirit of type definitions  and  Cray  Research  decided  to
force  evaluation  of  the  expression  at the time the type
definition itself is declared.


6.5.7 (3.5.7) Initialization

Constraints

     The type of the entity to be initialized  shall  be  an
object  type or an array of unknown size, but not a variable
length array type.

Example 19

  int n;
  main() {
     int a[n] = {1, 2};    /* error */
     int (*p)[n] = &a;     /* ok - pointer is scalar of fixed size */
  }



6.6 (3.6) Statements

A full expression is an  expression  that  is  not  part  of
another expression.  Each of the following is a full expres-
sion:  a variably qualified declarator; an initializer;  the
expression  in  an  expression  statement;  the  controlling
expression of  a  selection  statement  (if or switch);  the
controlling  expression  of a while or do statement; each of
the three (optional) expressions of  a  for  statement;  the
(optional)  expression  in a return statement.  The end of a
full expression is a sequence point.

Example 20

  {
     int n = 3, m= 4;
     int a[n++][n++];     /* order of evaluation is undefined */
     int b[m++], c[m++];  /* order of evaluation is defined   */
  }


Rationale:

     Since sequence points control the order  of  evaluation
of expressions with side effects, it seems reasonable to add
a sequence point after every variably qualified  declarator.
This specifies the behavior of declarations such as:

  int x[n++];
  int y[n++];




                           - 15 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


in that the side effects must be evaluated at the end of the
declarator.  However, the behavior of a declaration such as:

  int z[n++][n++];

is not specified because there is  no  guaranteed  order  of
evaluation  until  the  declarator  is complete.  This seems
consistent with an expression such as

  z[n++][n++]

in which there is no guaranteed order of evaluation either.


6.6.4.2 (3.6.4.2) Switch statement

Constraints

     The controlling expression shall not cause a  block  to
be  entered  by a jump from outside the block to a statement
that follows a case or default label in  the  block  (or  an
enclosed  block) that contains the declaration of a variably
qualified object or variably qualified typedef name.

Example 21

  int i;
  main() {
     int n = 10;
     switch (n) {    /* error - bypasses declaration of a[n] */
        int a[n];
        case 10:
           a[0] = 1;
           break;
        case 20:
           a[0] = 2;
           break;
     }
     switch (i) {    /* OK - declaration of b[n] not bypassed */
        case 0:
           {
              int b[n];
              b[2] = 4;
           }
           break;
        case 1:
           break;
     }
  }

ISSUE

Overview:




                           - 16 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


     The concern here is that a switch will  bypass  evalua-
tion  of the arbitrary integer expressions associated with a
variably qualified type.  In the case of  a  simple  pointer
that  is  variably  qualified  this  could  cause  incorrect
pointer arithmetic.  In the case of a variable length  array
type  this  is even more serious in that the variably quali-
fied object itself is not even allocated.

Rationale:

     Disallowing use of variably qualified objects  in  this
fashion  seems  reasonable  and a compile time diagnostic is
issued.


6.6.6.1 (3.6.6.1) Goto statements

Constraints

     The identifier in a goto statement shall name  a  label
located  somewhere in the enclosing function.  A goto state-
ment shall not cause a block to be entered by  a  jump  from
outside the block to a labeled statement in the block (or an
enclosed block) that contains the declaration of a  variably
qualified object or variably qualified typedef name.

Example 22

  void func(int n) {
       int j = 4;
       goto lab3;      /* error - going INTO scope of variable length array */
       {
          double a[n];
          a[j] = 4.4;
  lab3:
          a[j] = 3.3;
          goto lab4;   /* OK - going WITHIN scope of variable length array */
          a[j] = 5.5;
  lab4:
          a[j] = 6.6;
       }
       goto lab4;      /* error - going INTO scope of variable length array */
       return;
    }

ISSUE

Overview:

     The concern here is that a goto will bypass the evalua-
tion  of the arbitrary integer expressions associated with a
variably qualified type.  In the case of  a  simple  pointer
that  is variably qualified this causes wrong pointer arith-
metic.  In the case of a variable length array  object,  the



                           - 17 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


object itself would not even be allocated.

Rationale:

     Disallowing use of variably qualified objects  in  this
fashion  seems  reasonable  and a compile time diagnostic is
issued.


6.7.1 (3.7.1) Function Definitions

On entry to the function all size  expressions  of  variably
qualified parameters are evaluated.


7.6.2.1 (4.6.2.1) The longjmp function

Description

     If a longjmp function invocation causes the termination
of  a  function  or  block  in  which  variable length array
objects are still allocated, then the behavior is undefined.

ISSUE

Overview:

     The longjmp function that returns control back  to  the
point of the setjmp invocation might cause memory associated
with a variable length array object to be squandered.   Con-
sider the following program:


























                           - 18 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007



Example 23

  #include <setjmp.h>
  void g(int n);
  void h(int n);
  int n = 6;

  void f() {
     int x[n];        /* OK - `f' is not terminated         */
     setjmp (buf);
     g(n);
  }

  void g(int n) {
     int a[n];        /* undefined - `a' is still allocated */
     h(n);
  }

  void h(int n) {
     int b[n];        /* undefined - `b' is still allocated */
     longjmp(buf,2);  /* causes undefined behavior          */
  }

In this program the function h might  cause  storage  to  be
lost  for  the  variable  length array object a, declared in
function g (whose existence is unknown  to  h).   Additional
storage  could  be  lost  for variable length array object b
declared in function h.   In  this  case  an  implementation
knows that a longjmp is being performed in the presence of a
variable length array  object  whose  storage  needs  to  be
managed  accordingly.  However, since longjmp is a function,
it can be invoked through a ``pointer to function'' and thus
h  would  have no knowledge of the longjmp invocation occur-
ring.

Rationale:

     The Cray Research implementation ensures that any vari-
able  length  array  type objects are placed on the existing
stack first and heap storage is used only if room cannot  be
found  on the stack.  Thus, unless things get really big, no
space will be wasted because only vacant space on the  stack
was used initially.













                           - 19 -





 1/22/93         Arrays of Variable Length    X3J11.1/93-007


LANGUAGE SYNTAX

A.1.2.2 Declarations

(6.5.4), (3.5.4) direct-declarator:

identifier

(declarator)

direct-declarator [assignment-expression "opt"]

direct-declarator [* "opt"]

direct-declarator (parameter-type-list)

direct-declarator (identifier-list "opt")


(6.5.5), (3.5.5) direct-abstract-declarator:

(abstract-declarator)

direct-abstract-declarator [assignment-expression "opt"]

direct-abstract-declarator [* "opt"]

direct-abstract-declarator (parameter-type-list "opt")





























                           - 20 -





More information about the Numeric-interest mailing list