On 11/14/18 4:32 PM, Neia Neutuladh wrote:
On Wed, 14 Nov 2018 13:40:46 -0500, Steven Schveighoffer wrote:
You don't think this is confusing?
enum A : int {
val
}
A a;
foo(a); // error: be more specific
int x = a;
foo(x); // Sure
I find this confusing:
void foo(int i) {}
void foo(ubyte b) {}
enum A : int { val = 0 }
foo(A.val); // calls foo(ubyte)
A a = A.val;
foo(a); // calls foo(int)
If it instead produced an error, the error would look like:
Error: foo called with argument types (E) matches both:
example.d(1): foo(int i)
and:
example.d(2): foo(ubyte i)
I'm reminded of my son, who I sometimes give him a glass of water when
he asks for it, and after I hand it to him, he says "should I drink this?"
To me, making the user jump through these hoops will be insanely
frustrating.
Or else:
Error: none of the overloads of foo are callable using
argument types (A), candidates are:
example.d(1): foo(int i)
example.d(2): foo(ubyte i)
These aren't the intuitively obvious thing to me, but they're not going to
surprise me by calling the wrong function, and there are obvious ways to
make the code work as I want. Of the two, I'd prefer the former.
I prefer the correct version which calls foo(int). I think truly this is
a misapplication of the overload rules, and can be fixed.
The intuitively obvious thing for me is:
* Don't use VRP to select an overload. Only use it if there's only one
candidate with the right number of arguments.
* Don't use VRP if the argument is a ctor, cast expression, or symbol
expression referring to a non-builtin. Maybe disallow with builtins.
* Don't use VRP if the argument is a literal with explicitly indicated type
(0UL shouldn't match to byte, for instance).
To me, an enum based on int is an int before it's a typeless integer
value. VRP should be trumped by type (which it normally is). I would say
an enum *derives* from int, and is a more specialized form of int. It is
not derived from byte or ubyte. For it to match those overloads *over*
int is surprising.
Just like you wouldn't expect the `alias this` inside a class to match
an overload over its base class.
Oh, crap, it actually does... But I think that's a different bug.
I think this would make things more as most people expect:
foo(A.val); // A -> int, but no A -> byte; calls foo(int)
foo(0); // errors (currently calls foo(int))
If you have foo(int) and for some reason you can't call it with foo(0),
nobody is going to expect or want that.
foo(0L); // errors (currently calls foo(ubyte))
foo(cast(ulong)0); // errors (currently calls foo(ubyte))
I'm actually OK with this being the way it is, or even if it called the
foo(int) version. Either way, as long as there is a clear definition of
why it does that.
And when there's only one overload:
void bar(byte b) {}
bar(A.val); // errors; can't convert A -> byte
bar(0); // type any-number and fits within byte, so should work
bar(0UL); // errors; explicit incorrect type
bar(0UL & 0x1F); // bitwise and expression can do VRP
bar("foo".length); // length is a builtin; maybe do VRP?
bar(byte.sizeof); // sizeof is a builtin; maybe do VRP?
I am OK with VRP calls, even in the case of the enum when there's no int
overload.
-Steve