But, if we were allowed to make a breaking change, then this is how I think it should work:

// in a module scope...

enum E { foo, bar };

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

// These cause a compile error "foo is ambiguous":
1) E foo = E.bar;
2) E foo = E.foo;
3) enum foo = E.bar;
4) immutable foo = E.bar;
5) immutable foo = initFoo(); // if we can't CTFE initFoo()

// These are fine:
1) enum foo = E.foo;
2) immutable foo = E.foo;
3) int foo = 42; // int isn't implicitly convertible to E
4) E Foo = E.bar;

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

struct Convertible
{
    E _value;

    alias _value this;
}

Convertible foo; // Compile error: foo is ambiguous

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

struct MyStruct(T)
{
    T foo = 123; // Fine, hides E.foo inside MyStruct scope

    void fun()
    {
        T foo = 42 // Fine, hides both MyStruct.foo and
                   // E.foo inside this function scope
    }
}

Here's the logic of it:
-----------------------
An enumeration of type E should be visible (like it was a module scope variable) if, and only if, it occurs in one of these "hot-spots" where a value of type E is expected (e.g. it could be in a template parameter list, function argument list, or in a case expression of a switch).

Inside these "hot-spots" the enumeration fights over visibility against variables. It loses that fight against function local and class/struct local variables which have the same name (and whatever type). But it wins the battle over visibility against module scope variables which have the same name but are not implicitly convertible to type E. Against global variables which are of type E or are implicitly convertible to type E, the battle over visibility ends in a tie, and that's a compile-time ambiguity error.

Reply via email to