<p>C89 had a requirement that all declarations must appear at the top of the scope before any statements:</p>
<pre><code>int func(void) {
    int x = 10;
    f();
    int y = 20; // Invalid
    // ...
}
</code></pre>
<p>However this requirement was easy to get around most of the time, either by simply moving declarations to the top, and initializing them out of line if their initializer expressions were order-sensitive to the code in the statements, or one could just put a scope around them:</p>
<pre><code>int func(void) {
    int x = 10;
    f();
    {
        int y = 20; // Valid
        // ...
    }
}
</code></pre>
<p>Given that the C99 requirement to allow mixed declarations did not cause any problems (that I am aware of, as in this could not have possibly broken legacy code, etc.) and that identical semantics could be easily achieved by using the <code>{}</code> hack above, what prevented old C89 compilers from handling mixed declarations?</p>

<c><scoping><variables><history><declarations>
Why are mixed declarations more challenging to implement than forcing all declarations to be at the top of a scope?
<h1>One pass compilation</h1>
<p>C was originally (and, as far as I know, still is) designed to allow for one pass compilation. This means you tokenize, parse, and output, all in one pass, never building an intermediate structure like an ΑST. As you can imagine, this is quite good on memory usage, although a fair bit more complex than building an AST. This was a reasonable compromise back when memory was expensive.</p>
<p>Therefore, if one restricts all variable declarations to the top of the scope, one knows how big the stack should be <em>for the whole scope</em>. You never have to re-adjust the stack in the middle of a scope [1]; you only extend it at the start of the scope, and unextend it at the end of the scope. Of course, one can still allow variable decls in the middle of a scope while maintaining one-pass compilation support - it's just more complex. By forcing you to declare all your variables at the start of a scope, it makes it a fair bit easier to figure out those stack transforms.</p>
<p>[1]: Remembering that if you're doing one pass compilation, you can't use lookahead or AST traversal to figure out all the variables in a scope and transform the stack at the start of the scope accounting for all the variables.</p>

CC BY-SA 4.0
2023-11-07T02:49:50.887Z
2023-11-07T02:49:50.887Z
blueberry
CC BY-SA 4.0
2023-11-07T11:43:42.557Z
you could use back patching (like you would need to do for some structured control flow) to adjust the stack size though
ratchet freak
CC BY-SA 4.0
2023-11-07T16:25:06.303Z
You could just treat a declaration in the middle of a block as if it started a nested block that extends to the end of the original block.
Barmar
CC BY-SA 4.0
2023-11-07T18:40:15.300Z
@Barmar: That would yield semantics inconsistent with the C Standard if both a `goto` label and a forward `goto` statement appear above the declaration, and if code which branches to the first label relies upon the declared object's value being indisturbed after execution reaches the target of the forward `goto`.
supercat
CC BY-SA 4.0
2023-11-07T18:43:34.237Z
@ratchetfreak: I would think even something like an 8080-based Altair with a paper tape reader and punch could probably have supported a very practical development system using a purpose-designed high-level language that would allow programs to be processed with one pass through the source tape, yielding a destination tape that could be loaded directly, applying back patches at load time.  I wonder if any dev tools for such systems were designed that way.
supercat
CC BY-SA 4.0
2023-11-07T22:36:02.797Z
Except it doesn't work. You can have a function call initializer.
Joshua
CC BY-SA 4.0
2023-11-08T20:15:11.640Z
Except a) you only need total size of local variables to set up the activation record, and since you need backpatching for loops anyway, you can backpatch that too; b) single-pass compilers require more memory than multi-pass ones, not less: P.B. Hansen discusses that in his "On Pascal Compilers", pp. 18-22: a single-pass version of the compiler described requires 61K words (for both the compiler's code itself and the working space) to compile itself; a three-pass version uses at most 16K words during the largest pass while compiling the code for the compiler's largest pass.
Joker_vD
CC BY-SA 4.0
2023-11-08T22:32:07.520Z
And besides all of that: single-pass compilers tend to spill temporaries (from evaluating expressions) to stack like crazy and somehow they don't need to know how much of those will be required, or encounter troubles with balancing the stack — and the same exact code can handle introduction of locals in the middle of the block and discarding them at the end of the block with minimal modifications.
Joker_vD
CC BY-SA 4.0
2023-11-08T23:22:41.787Z
@Joker_vD: If one uses back-patching for stack frame setup, one will likely need to generate worst-case setup code and incur a performance penalty if a smaller one could have sufficed.  Maybe one could move the function's entry point and backpatch the function's initialization code to start with some skipped dummy bytes if the longer form wasn't needed, but I don't think I've ever seen anything like that done.
supercat
CC BY-SA 4.0
2023-11-08T23:24:54.347Z
@Joker_vD: A multi-pass compiler which only jettisons the code necessary to handle each pass before loading the code for the next pass may get by with less memory, but would take much longer to process code.  The 61K figure for a Pascal compiler seems excessive, given that Turbo Pascal got by with a lot less than that, despite having a rather nice built-in text editor.
supercat
<p>It makes stack management easier, because you do stack layout and then you do other things in the body, but you do not have to deal with changing the stack layout in the middle of a function. This sounds extremely stupid, but it makes code really easy. It's kind of hard to understand these days if you build a language yourself, because you just dump everything into LLVM and can expect it to generate good code for you. If you have to do everything yourself and if writing code is a lot more time consuming, because there weren't exactly any good tools available back then, strange simplifications like this are actually plausible.</p>

CC BY-SA 4.0
2023-11-07T19:54:40.180Z
2023-11-07T19:54:40.180Z
feldentm
CC BY-SA 4.0
2023-11-07T22:38:44.677Z
`char * a = malloc(4096); size_t b = myfunction(a);` It's like there's a ghost rule that says can't call functions in initializers.
Joshua
CC BY-SA 4.0
2023-11-08T16:55:28.397Z
You can execute any code in a declaration.
gnasher729
CC BY-SA 4.0
2023-11-08T18:32:55.520Z
@gnasher729: Some early compilers didn't allow that, and such non-allowance simplified compiler design.  Allowing executable code in initialization expressions but not allowing other statements required that compiler writers do 90% of the work necessary to support more general placements, while offering programmers less than 90% of the benefit.
supercat
<p>There is a syntax for declaration, and a syntax for statement. In old C versions the contents of a block was zero or more declarations, followed by zero or more statements. In newer C versions it’s zero or more declarations or statements. That’s the whole difference.</p>
<p>Forcing declarations first doesn’t make anything easier. Consider that in T var = expression the expression can be a comma expression made of 100 subexpressions, can contain function calls, and be arbitrary complicated. With the ?: operator and recursive calls I wouldn’t be surprised if you can replace any statements other than perhaps goto with declarations. So a compiler for old C must handle anything a compiler for new C can handle.</p>

CC BY-SA 4.0
2023-11-08T16:50:51.640Z
2023-11-08T16:50:51.640Z
gnasher729
<p>In languages which require that all variable declarations within a code block start with a recognizable reserved word, and requires that initialization expressions be compile-time constants, a compiler could generate code for a function which sets up a stack frame and initializes any objects with constant values before generating any other code for that funcion.  Such code would thus be usable even on single-pass build systems that need to produce a code image directly.  If a compiler is targeting a three-pass assembler, it might be able to do something like:</p>
<pre><code>_Foo:
  push  bp
  mov   bp,sp
  sub   sp,ssize_Foo
</code></pre>
<p>and define the size later on in the code, and such a design might work on a two-pass assembler if the target platform would only have one operand size mode for the SUB or if the compiler always used the largest one, but letting the compiler choose among subtract-byte, subtract-word, or don't-subtract-anything operations before outputting code would make things more efficient.</p>
<p>The C Standard ended up mandating most of the complexity necessary to accommodate mid-block declarations by allowing initialization expressions to contain executable code, and one might reasonably argue that once initialization expressions could contain code there was no longer much advantage to forbidding mid-block declarations.  Requiring that declarations precede the first branch target within a function still had value, however, which got lost in C99.  Given something like:</p>
<pre><code>int test(int x)
{
   int arr[x];
   label1:
     ... do some stuff
     goto label3:
   label2:
     ...
</code></pre>
<p>a compiler would have no way of knowing whether the lifetime of anything which is declared after <code>label2</code> might extend into code between <code>label1</code> and label2 until it finished parsing the entire function, and would thus not be able to determine the offset of <code>arr</code> until then.</p>
<p>IMHO, many of the purposes that would be served by mid-block declarations could be better served by a &quot;temporary declaration&quot; syntax, which would define things until the next &quot;end temporary scope&quot; directive <em>which would behave independently from normal block scope</em>.  If one wants to compute e.g. <code>(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)</code>, being able to declare temporary objects to hold <code>x2-x1</code> and <code>y2-y1</code> whose lifetime ends before whatever was being initialized with that expression would be better than having such declarations always extend through the end of the block.</p>

CC BY-SA 4.0
2023-11-07T18:37:05.937Z
2023-11-07T18:37:05.937Z
supercat
# Why are mixed declarations more challenging to implement than forcing all declarations to be at the top of a scope?
Posted by: username_0 on 2023-11-07
C89 had a requirement that all declarations must appear at the top of the scope before any statements:
int func(void) {
    int x = 10;
    f();
    int y = 20; // Invalid
    // ...
}

However this requirement was easy to get around most of the time, either by simply moving declarations to the top, and initializing them out of line if their initializer expressions were order-sensitive to the code in the statements, or one could just put a scope around them:
int func(void) {
    int x = 10;
    f();
    {
        int y = 20; // Valid
        // ...
    }
}

Given that the C99 requirement to allow mixed declarations did not cause any problems (that I am aware of, as in this could not have possibly broken legacy code, etc.) and that identical semantics could be easily achieved by using the {} hack above, what prevented old C89 compilers from handling mixed declarations?
**Tags:** c scoping variables history declarations
---
## 4 answers
### Answer by username_1 on 2023-11-07. Score: 17
# One pass compilation

C was originally (and, as far as I know, still is) designed to allow for one pass compilation. This means you tokenize, parse, and output, all in one pass, never building an intermediate structure like an ΑST. As you can imagine, this is quite good on memory usage, although a fair bit more complex than building an AST. This was a reasonable compromise back when memory was expensive.

Therefore, if one restricts all variable declarations to the top of the scope, one knows how big the stack should be *for the whole scope*. You never have to re-adjust the stack in the middle of a scope [1]; you only extend it at the start of the scope, and unextend it at the end of the scope. Of course, one can still allow variable decls in the middle of a scope while maintaining one-pass compilation support - it's just more complex. By forcing you to declare all your variables at the start of a scope, it makes it a fair bit easier to figure out those stack transforms.

[1]: Remembering that if you're doing one pass compilation, you can't use lookahead or AST traversal to figure out all the variables in a scope and transform the stack at the start of the scope accounting for all the variables.
> **Comments:**
> **username_2** (2023-11-07): you could use back patching (like you would need to do for some structured control flow) to adjust the stack size though  
> **username_3** (2023-11-07): You could just treat a declaration in the middle of a block as if it started a nested block that extends to the end of the original block.  
> **username_4** (2023-11-07): @Barmar: That would yield semantics inconsistent with the C Standard if both a `goto` label and a forward `goto` statement appear above the declaration, and if code which branches to the first label relies upon the declared object's value being indisturbed after execution reaches the target of the forward `goto`.  
> **username_4** (2023-11-07): @ratchetfreak: I would think even something like an 8080-based Altair with a paper tape reader and punch could probably have supported a very practical development system using a purpose-designed high-level language that would allow programs to be processed with one pass through the source tape, yielding a destination tape that could be loaded directly, applying back patches at load time.  I wonder if any dev tools for such systems were designed that way.  
> **username_5** (2023-11-07): Except it doesn't work. You can have a function call initializer.  
> **username_6** (2023-11-08): Except a) you only need total size of local variables to set up the activation record, and since you need backpatching for loops anyway, you can backpatch that too; b) single-pass compilers require more memory than multi-pass ones, not less: P.B. Hansen discusses that in his "On Pascal Compilers", pp. 18-22: a single-pass version of the compiler described requires 61K words (for both the compiler's code itself and the working space) to compile itself; a three-pass version uses at most 16K words during the largest pass while compiling the code for the compiler's largest pass.  
> **username_6** (2023-11-08): And besides all of that: single-pass compilers tend to spill temporaries (from evaluating expressions) to stack like crazy and somehow they don't need to know how much of those will be required, or encounter troubles with balancing the stack — and the same exact code can handle introduction of locals in the middle of the block and discarding them at the end of the block with minimal modifications.  
> **username_4** (2023-11-08): @Joker_vD: If one uses back-patching for stack frame setup, one will likely need to generate worst-case setup code and incur a performance penalty if a smaller one could have sufficed.  Maybe one could move the function's entry point and backpatch the function's initialization code to start with some skipped dummy bytes if the longer form wasn't needed, but I don't think I've ever seen anything like that done.  
> **username_4** (2023-11-08): @Joker_vD: A multi-pass compiler which only jettisons the code necessary to handle each pass before loading the code for the next pass may get by with less memory, but would take much longer to process code.  The 61K figure for a Pascal compiler seems excessive, given that Turbo Pascal got by with a lot less than that, despite having a rather nice built-in text editor.  
---
### Answer by username_4 on 2023-11-07. Score: 3
In languages which require that all variable declarations within a code block start with a recognizable reserved word, and requires that initialization expressions be compile-time constants, a compiler could generate code for a function which sets up a stack frame and initializes any objects with constant values before generating any other code for that funcion.  Such code would thus be usable even on single-pass build systems that need to produce a code image directly.  If a compiler is targeting a three-pass assembler, it might be able to do something like:
_Foo:
  push  bp
  mov   bp,sp
  sub   sp,ssize_Foo

and define the size later on in the code, and such a design might work on a two-pass assembler if the target platform would only have one operand size mode for the SUB or if the compiler always used the largest one, but letting the compiler choose among subtract-byte, subtract-word, or don't-subtract-anything operations before outputting code would make things more efficient.
The C Standard ended up mandating most of the complexity necessary to accommodate mid-block declarations by allowing initialization expressions to contain executable code, and one might reasonably argue that once initialization expressions could contain code there was no longer much advantage to forbidding mid-block declarations.  Requiring that declarations precede the first branch target within a function still had value, however, which got lost in C99.  Given something like:
int test(int x)
{
   int arr[x];
   label1:
     ... do some stuff
     goto label3:
   label2:
     ...

a compiler would have no way of knowing whether the lifetime of anything which is declared after label2 might extend into code between label1 and label2 until it finished parsing the entire function, and would thus not be able to determine the offset of arr until then.
IMHO, many of the purposes that would be served by mid-block declarations could be better served by a "temporary declaration" syntax, which would define things until the next "end temporary scope" directive which would behave independently from normal block scope.  If one wants to compute e.g. (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1), being able to declare temporary objects to hold x2-x1 and y2-y1 whose lifetime ends before whatever was being initialized with that expression would be better than having such declarations always extend through the end of the block.
---
### Answer by username_7 on 2023-11-07. Score: 1
It makes stack management easier, because you do stack layout and then you do other things in the body, but you do not have to deal with changing the stack layout in the middle of a function. This sounds extremely stupid, but it makes code really easy. It's kind of hard to understand these days if you build a language yourself, because you just dump everything into LLVM and can expect it to generate good code for you. If you have to do everything yourself and if writing code is a lot more time consuming, because there weren't exactly any good tools available back then, strange simplifications like this are actually plausible.
> **Comments:**
> **username_5** (2023-11-07): `char * a = malloc(4096); size_t b = myfunction(a);` It's like there's a ghost rule that says can't call functions in initializers.  
> **username_8** (2023-11-08): You can execute any code in a declaration.  
> **username_4** (2023-11-08): @gnasher729: Some early compilers didn't allow that, and such non-allowance simplified compiler design.  Allowing executable code in initialization expressions but not allowing other statements required that compiler writers do 90% of the work necessary to support more general placements, while offering programmers less than 90% of the benefit.  
---
### Answer by username_8 on 2023-11-08. Score: 1
There is a syntax for declaration, and a syntax for statement. In old C versions the contents of a block was zero or more declarations, followed by zero or more statements. In newer C versions it’s zero or more declarations or statements. That’s the whole difference.

Forcing declarations first doesn’t make anything easier. Consider that in T var = expression the expression can be a comma expression made of 100 subexpressions, can contain function calls, and be arbitrary complicated. With the ?: operator and recursive calls I wouldn’t be surprised if you can replace any statements other than perhaps goto with declarations. So a compiler for old C must handle anything a compiler for new C can handle.
---
