Variable Declaration Restrictions

Comments

I came across a surprising restriction on variable declarations that I thought I would share.

My own investigation into C# was prompted by an answer given to a Stack Overflow question regarding an odd Objective-C compiler error.  The answer seemed to allude to a language specification somewhere that doesn’t allow variable declarations in one-liner embedded statements of control structures (loops, switch statements, if-else statements, etc.).  Not only that, Objective-C wasn’t unique in this restriction; it appears Java and even C itself have had this restriction for quite a while as well.

Naturally this got my curiosity going and I decided to see if C# had also carried over such a restriction.  The answer is yes; it appears that Java, C#, and Objective-C have all kept this language restriction from their common ancestor C.  Let’s get right to what it looks like:

code_screenshot

The red-underlined errors read “embedded statement cannot be a declaration or labeled statement” and the green-underlined warnings read “the variable ‘i’ is assigned but its value is never used.”  You’ve probably seen the warning quite a few times before, but the compiler error really caught me by surprise.  I mean it makes sense that declaring a variable in the one-line body of a control structure is pointless, as it would immediately go out of scope (and usefulness) after declared.  But I thought it odd for the compiler to actually deny such a program statement, as if it put the compiler in an invalid state or maybe it was somehow dangerous.

One thing I do want to point out that appears to be different from the other languages (or at the very least Objective-C) is that the variable declaration is allowed in the switch statement, as illustrated in the image above.

To figure out why this isn’t allowed, I decided to actually look into the C# 3.0 Language Specification to see if there were any comments or explanations regarding this issue.  I ended up finding the C# language grammar in Appendix B.  I was able to parse through the grammar and find this restriction in the grammar itself.  Take a look:

B.2.5 Statements

statement:
labeled-statement
declaration-statement
embedded-statement

embedded-statement:
block
empty-statement
expression-statement
selection-statement
iteration-statement
jump-statement
try-statement
checked-statement
unchecked-statement
lock-statement
using-statement
yield-statement

block:
{ statement-listoptional }

statement-list:
statement
statement-list statement

selection-statement:
if-statement
switch-statement

if-statement:
if ( boolean-expression ) embedded-statement
if ( boolean-expression ) embedded-statement else embedded-statement

switch-statement:
switch ( expression ) switch-block

switch-block:
{ switch-sectionsoptional }

switch-sections:
switch-section
switch-sections switch-section

switch-section:
switch-labels statement-list

switch-labels:
switch-label
switch-labels switch-label

switch-label:
case constant-expression :
default :

You’ll notice that there is the concept of a statement and an embedded-statement; also note that statement is a superset of embedded-statement, adding two other types of statements: labeled-statement and declaration-statement.  If a particular grammar rule requires an embedded-statement, then those two latter types of statements are not allowed in that context.  If we wanted to allow those two statement types, our grammar rule should specify the generic statement category instead.

Also noteworthy is that an embedded-statement can resolve to a block, which includes braces around a statement-list.  A statement-list is one or more statement entities, which would now allow us to use the two statement types excluded from embedded-statement.

Something interesting that is not included in the grammar snippet above, the following statement constructs (and language keywords) require the block statement construct, bypassing the option of a grammar resolution to embedded-statement directly and allowing you to leave off surrounding braces: try, catch, finally, checked, and unchecked.

Almost all of the other statement constructs allow you the option of a one-liner embedded-statement (or which can be resolved into a block), including if, if-else, while, do-while, for, and foreach.  The one surprising exception is the switch construct, which allows for a statement-list after each switch-label.  As a side note, this allows you to put useless braces (via block) around your statement-list (or anywhere inside it) after any of your switch-label statements if you wanted.  But back to the point, these facts explain the existence of (and in one case, lack of) compiler errors regarding embedded statements in the code screenshot near the top.

Now you will know exactly what the compiler means when it gives you the error that an “embedded statement cannot be a declaration or labeled statement.