After translating some buggy C code (full of gotos and global variables) to D 
and going hunting for bugs for some time, I have grown a desire to write this 
post.

Variable name hiding is a source of bugs. A typical example:


int x;
void foo() {
    int x = 5;
    // lot of code code here
    x++;
}


The local name 'x' hides the global variable name 'x'. Sometimes the programmer 
thinks she is using a global variable, when instead she is using the local one, 
of vice versa. Coding guidelines suggest to rename such local variable x to 
avoid possible future troubles, and I agree with this advice.

Some ways to face this problem:

1) Do nothing.
2) Give optional explicit information about the source of all variable names 
used in a function, using the optional @outer() I have proposed elsewhere.
3) Statically disallow (gives an error) local variables from shadowing global 
ones.
4) Produce a warning when a local variable shadows a global ones, in a smart 
way.
5) As the leading dot to denote global variables, require the use of another 
leading symbol (or some other mean) to denote local variable, when there is 
ambiguity.

--------------

Regarding option 1, in D global variables are uncommon if you follow modern 
programming practices, and if you keep functions short it's easy to see if they 
are shadowing an outer name.
But the situation is sometimes different if you are porting legacy C code to D, 
or if you have bad D programmers :-)

--------------

Option 3 is not so bad, D language already contains some examples of this:

struct Foo { int j; }
void main() {
    foreach (i; 0 .. 10)
        foreach (i; 0 .. 10) {} // error
    Foo f;
    int j;
    with (f) {
        j++; // error
    }
}

--------------

Option 4 too is not bad, especially if some care is given to avoid producing 
warnings where they are not needed:


int x, y, z;
void main() {
  int x;
  .x++; // no warning, the scope is known
  x++; // warning
  string y;
  y = "foo"; // no warning, .y y have incompatible types
  int z;
  float z;
  z++; // warning, int is implicitly convertible to float
}

--------------

Option 5 means requiring explicit scope information when there there is a 
collision:

int x;
void main() {
  int x;
  .x++; // no error, the scope is known
  main.x++; // no error, the scope is known
}

--------------

I have discussed about hiding global variables from local ones, but D allows 
nesting of functions, so this discussion applies to such variables too:


void main() {
    int x;
    void foo() {
        int x;
        // code here
        x++;
    }
}

Bye,
bearophile

Reply via email to