Implicit enum conversions are a stupid PITA

Nick Sabalausky a at a.a
Tue Mar 23 14:47:09 PDT 2010


I'm bringing this over here from a couple separate threads over on "D.learn" 
(My "D1: Overloading across modules" and bearophile's "Enum equality test").

Background summary:

bearophile:
> I'm looking for D2 rough edges. I've found that this D2 code
> compiles and doesn't assert at runtime:
>
> enum Foo { V1 = 10 }
> void main() {
>  assert(Foo.V1 == 10);
> }
>
> But I think enums and integers are not the same type,
> and I don't want to see D code that hard-codes comparisons
> between enum instances and number literals, so I think an
> equal between an enum and an int has to require a cast:
>
> assert(cast(int)(Foo.V1) == 10); // OK

He goes on to mention C++0x's "enum class" that, smartly, gets rid of that 
implicit conversion nonsense.

To put it simply, I agree with this even on mere principle. I'm convinced 
that the current D behavior is a blatant violation of strong-typing and 
smacks way too much of C's so-called "type system".

But here's another reason to get rid it that I, quite coincidentally, 
stumbled upon right about the same time:

Me:
> In D1, is there any reason I should be getting an error on this?:
>
> // module A:
> enum FooA { fooA };
> void bar(FooA x) {}
>
> // module B:
> import A;
> enum FooB { fooB };
> void bar(FooB x) {}
>
> bar(FooB.fooB); // Error: A.bar conflicts with B.bar (WTF?)

In the resulting discussion (which included a really hackish workaround), it 
was said that this is because of a rule (that I assume exists in D2 as well) 
that basically goes "two functions from different modules are in conflict if 
they have the same name." I assume (and very much hope) that the rule also 
has a qualification "...but only if implicit conversion rules make it 
possible for one to hijack the other".

It was said that this is to prevent a function call from getting hijacked by 
merely importing a module (or making a change in an imported module). That I 
can completely agree with. But I couldn't understand why this would cause 
conflicts involving enums until I thought about implicit enum-to-base-type 
conversion and came up with this scenario:

// Module Foo:
enum Foo { foo }

// module A:
import Foo;
void bar(Foo x){}

// module B version 1:
import Foo; // Note: A is not imported yet
void bar(int x){}
bar(Foo.foo); // Stupid crap that should never be allowed in the first place

// module B version 2:
import Foo;
import A; // <- This line added
void bar(int x){}
bar(Foo.foo); // Now that conflict error *cough* "helps".

So thanks to the useless and dangerous ability to implicitly convert an enum 
to its base type, we can't have certain perfectly sensible cross-module 
overloads.

Although, frankly, I *still* don't see why "bar(SomeEnum)" and 
"bar(SomeOtherEnum)" should ever be in conflict (unless that's only D1, or 
if implicit base-type-to-enum conversions are allowed (which would make 
things even worse)).





More information about the Digitalmars-d mailing list