More name hiding

bearophile bearophileHUGS at lycos.com
Fri Aug 19 08:07:12 PDT 2011


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


More information about the Digitalmars-d mailing list