REPOST: reaction to Variable Length Array Proposal

D. Hugh Redelmeier uunet!mimosa.com!hugh
Wed Feb 10 14:38:27 PST 1993


[I sent this earlier today to SC22WG14adkuug.dk and ncegacray.com,
but some copies of it bounced.  I'm sending this to
ncegacray.com.  This is *not* revised, so if you think you have
seen it, then you have.  I appologize for any inconvenience this may
cause you.]

These are my reactions to the VLA proposal.  In order to respond
quickly, I have not been as careful as I would have liked.

I strongly agree with most of RFG's comments.  I will try not to
repeat them.

Before I get too critical, I would like to thank TAM for the effort
he has put into creating this document.  I think that these ideas
are worth exploring, even if I don't support their adoption.

Hugh Redelmeier
hughamimosa.com or {utcsri, uunet!attcan, utzoo, scocan}!redvax!hugh
When all else fails: hughacsri.toronto.edu
+1 416 482-8253

GENERAL COMMENTS
================

I think that the VLA approach suffers from useless redundancy.  In
particular, arrays and their dimensions are separated (i.e. the
array, and the free variables used in its size expression).  The
programmer has to specify both, both in arguments and in parameters;
either one might be wrong, and this mismatch will generally not be
detected by the implementation.  It seems clumsy and dangerous.  I
haven't looked at the "fat pointers" proposal recently, but I know
that it bundles the (pointer to the) array and the size in one unit.

Certain VLA limitations seem to be due to the loose coupling of the
array bound with the array.  In particular, the omission of static
objects of VLA type seems to be because it is not clear how to write
the array bound expression.  With a fat pointer type, the bound is
part of the object and thus has an identical lifetime.

If VLAs could be members of structs, with the free variables being
constrained to being components of the same struct, I think that fat
pointers could be simulated by such structs.  Unfortunately, the
meaning of the VLA size expression would have to differ: it would
have to be evaluated at some time(s) other than the "execution" of
the declaration.  I think fat pointers capture this idea better.

The VLA approach closely matches the FORTRAN approach.  I feel that
this is the strongest argument in its favour, but I don't think it
should override good language design principles.

If your desire is to match the calling sequence for FORTRAN, say
so.  In fact, I don't think this is expressible in the C standard --
for good reasons.  If you want ad hoc compatibility, I seem to
remember that a previous fat-pointer paper showed how to code it in
fat-pointer terms (using casts).  This might seem clumsy, but the
unstated requirement for backward compatibility with another
language is pretty over-specified and hence awkward.

If we're going to add this capability to C, lets get all the power
and elegance we can for the least cost.  Let's not be locked into
mimicing a living fossil.


SPECIFIC COMMENTS
=================

- from Tom's proposal:

	| 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.

  I think that this wording must be extended to allow empty.  I
  think that "constant expression" should read "integral constant
  expression".

- What is the reason for distinguishing [*] from []?  Currently, []
  is allowed only as the leftmost subscript, but this could be
  extended instead of adding [*], but with the same meaning.  I
  don't much care for the [*] notation, partly because it requires
  extra look-ahead.

- From the VLA proposal:
	| 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.

  [This last sentence seems to be missing an "s" in "ued" and one in
  "pecify".]

  I think that the wording needs refinement to allow [*] within
  function definitions.  For example:

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

  This looks like a simple oversight, but I think it would be good
  to simplify and generalize the rule while fixing it.  It would be
  reasonable to allow [*] anywhere a VLA is appropriate.  The
  restriction should be that the array size must be known if it is
  needed to perform subscripting, pointer arithmetic, or sizeof
  operations specified by the program (and any similar operations
  that I have forgotten).  Perhaps this could be an extension of the
  incomplete type concept.

- In the existing C standard, the context of an expression dictates
  whether it must be a constant expression or not.  In particular,
  the array size of an array declarator is such a context (actually,
  that context demands an integral constant expression, but this
  fact seems to be lost in the VLA proposal).  The VLA changes this:
  whether an array size expression is constant or not must be
  determined from the expression itself.

  The existing definition of constant expression is written very
  loosely, and then the semantics puts restrictions on what the
  programmer may write.  It is not safe to re-interpret these
  restrictions as definitional.

  Consider the following fragment:

	void foo() {
		int	bar[1/0];
	}

  I think that the current standard requires this division by zero
  to be diagnosed at compile-time.  Under the VLA proposal, I think
  that this function is perfectly legal as long as it is not
  invoked.

  Consider the following fragment:

	static double cosine_tab[(int) (256 * 3.14159) + 1];

  This is not currently strictly conforming, but it violates no
  constraints.  Many implementations have extended constant
  expressions to allow this useful case.  I think that under the VLA
  proposal, this would be a VLA that was static, hence a constraint
  violation.  So currently legal programs would become illegal.

  My compiler will only fold floating-point expressions in a context
  where a constant is required.  An array size expression in a
  declarator is such a context, but would not be under the VLA
  proposal.

  In general, I think the right approach would be to rework the
  definition of (integral?) constant expression so that it is up to
  its new duties.  This does not eliminate all of these problems.

- From VLA 6.5.4.3:

	| 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.

  I feel that use of identifiers before their declaration violates
  "the spirit of C".  Furthermore, a whole bunch of questionable
  cases come to mind.  That most of them are clearly dealt with by
  the proposal is good, but not enough.  The fact that they arose
  makes me very uneasy; I am even more disturbed by the surprises.

	void foo(void) {
		long n = nn;
		void f(int a[n/*?*/], int n);	/* second n wins */
		...
	}

	void foo(void) {
		long n = nn;
		void f(int a[sizeof(n/*?*/)], int n);	/* first n wins */
		...
	}

	void foo(void) {
		/* second n wins twice! */
		long n = nn;
		void f(int a[sizeof(n/*?*/)], int a[n/*?*/], int n);
		...
	}

	void foo(void) {
		/* is this specified? */
		void f(int a[n], void (*g)(int a[n/*??*/]), int n);
		...
	}

	void foo(void) {
		/* is this specified? */
		void f(int a[n], void (*g)(int a[n/*??*/]; int n), int n);
		...
	}

	void foo(void) {
		/* well-defined but peculiar! */
		void f(int a[b[0]], int b[a[0]]);
		...
	}

	void foo(void) {
		/* well-defined, peculiar, and useful! */
		void f(int a[a[0]]);
		...
	}

  I really question the complexity and danger of this part of the
  proposal.

- A smaller issue about the same words, from VLA 6.5.4.3:

	| 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.

  Perhaps the restriction on depending on VLA size could be recast
  as saying that the array type is incomplete in its extended scope,
  or perhaps the whole prototype.  This is not proposed wording,
  just an idea on how to get at the problem that I think is being
  addressed.

- As a compiler writer, I very much dislike the extended scope.  It
  does great violence to the current structure of my compiler.  The
  simplest fix to my compiler will require duplicating and mutating
  large chunks of code.  Ugh.  I don't like RMS's proposal much, but
  it makes me much happier than this one.



More information about the Numeric-interest mailing list