Jump to page: 1 2
Thread overview
[Issue 19399] Different Conversion Rules for Same Value and Type -- Enum
Nov 14, 2018
Sprink
Nov 14, 2018
Simon Naarmann
Feb 14, 2019
David Eckardt
Feb 14, 2019
David Eckardt
Feb 15, 2019
David Eckardt
Feb 15, 2019
Simen Kjaeraas
Feb 15, 2019
David Eckardt
Jun 27, 2019
Dlang Bot
Jun 26, 2020
Walter Bright
Jun 26, 2020
Walter Bright
Jun 26, 2020
Walter Bright
Jun 26, 2020
Walter Bright
Jun 26, 2020
Walter Bright
Jun 26, 2020
Simen Kjaeraas
November 14, 2018
https://issues.dlang.org/show_bug.cgi?id=19399

Sprink <sprink.noreply@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           See Also|                            |https://issues.dlang.org/sh
                   |                            |ow_bug.cgi?id=10560

--
November 14, 2018
https://issues.dlang.org/show_bug.cgi?id=19399

Simon Naarmann <eiderdaus@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |eiderdaus@gmail.com

--- Comment #1 from Simon Naarmann <eiderdaus@gmail.com> ---
This looks like not-a-bug to me: VRP examines literals (A.a) and concludes that A.a fits in a byte. But VRP will not deduce from the code flow that v contains some value that fits in a byte.

The line `b = 128,` can be removed, the example will produce the same output
then on DMD64 D Compiler v2.083.0: The literal A.a goes to foo(byte) and A v
goes to foo(int) even when v == A.a.

--
February 14, 2019
https://issues.dlang.org/show_bug.cgi?id=19399

David Eckardt <david.eckardt@sociomantic.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |david.eckardt@sociomantic.c
                   |                            |om

--- Comment #2 from David Eckardt <david.eckardt@sociomantic.com> ---
This looks like a bug to me:

int f(ubyte x) { return x.sizeof; }
int f(ushort x)  { return x.sizeof; }

enum E: ushort {a = 10}

static assert(E.a.sizeof == E.sizeof); // succeeds
static assert(E.a.sizeof == f(E.a));  // fails, f(ubyte) is called

E.a.sizeof can be 1 or 2 depending on how it is used. This is obviously an error. My guess is that for a function call “E.a” is treated as if it was the numeric literal “10”, then typeof(10) is used to pick the function from the overloads.

--
February 14, 2019
https://issues.dlang.org/show_bug.cgi?id=19399

--- Comment #3 from David Eckardt <david.eckardt@sociomantic.com> ---
(In reply to David Eckardt from comment #2)
> int f(ubyte x) { return x.sizeof; }
> int f(ushort x)  { return x.sizeof; }
> 
> enum E: ushort {a = 10}
> 
> static assert(E.a.sizeof == E.sizeof); // succeeds
> static assert(E.a.sizeof == f(E.a));  // fails, f(ubyte) is called

Extended the example:

int f(ubyte x) { return x.sizeof; }
int f(ushort x)  { return x.sizeof; }

enum E: ushort {a = 10}

immutable y = E.a;

static assert(E.sizeof == 2); // succeeds
static assert(E.a.sizeof == E.sizeof); // succeeds
static assert(y.sizeof == E.sizeof); // succeeds
static assert(E.a.sizeof == f(E.a));  // fails, f(ubyte) is called

--
February 15, 2019
https://issues.dlang.org/show_bug.cgi?id=19399

--- Comment #4 from David Eckardt <david.eckardt@frequenz.io> ---
The wrong overloaded function is picked with arrays as well. An array of integers can be implicitly cast to char[]. This should not happen.


int f(const char[] str) {return 1;}
int f(const void[] data, int type_id) {return 2;}

assert(f("abc") == 1); // As expected.
assert(f("abc", 5) == 2); // As expected.

assert(f([1, 2, 3]) == 1); // Compiles - an array of integers matches
f(char[])?!
f([1, 2, 300]); // Error: None of the overloads of `f` matches, as expected

enum e = [4, 5, 6];
static assert(is(typeof(e) == int[])); // So we do have ints here...
assert(f(e) == 1);                     // ... and int[] matches f(char[])?!?

immutable a = e;
f(a, 7); // Compiles
f(a); // Error: None of the overloads of `f` matches, as expected

--
February 15, 2019
https://issues.dlang.org/show_bug.cgi?id=19399

Simen Kjaeraas <simen.kjaras@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |simen.kjaras@gmail.com

--- Comment #5 from Simen Kjaeraas <simen.kjaras@gmail.com> ---
I started out thinking this was not a bug, since enum values are basically inserted verbatim into the code. And indeed, this is exactly what should happen in cases like this:

int fun(ubyte) { return 0; }
int fun(ulong) { return 1; }

enum M = 10;
// Exactly equivalent to foo(10); - will call the ubyte overload.
static assert(fun(M) == 0);

However, this intuition breaks down when the enum has a specified type:

enum ulong N = 10;
// Will always call the ulong overload.
static assert(fun(N) == 1);

Both of these cases are working correctly. The error is with enums with curly brackets. If no type is specified for the enum, the behavior is correct:

enum O {
    O = 10
}
// Exactly equivalent to foo(10); - will call the ubyte overload.
static assert(fun(O.O) == 0);

However, when the type is explicitly specified, the error occurs:

enum P : ulong {
    P = 10
}
// Should call the ulong overload, but type information is discarded and the
ubyte overload is called instead. This assert will fail.
static assert(fun(P.P) == 1);

VRP, as pointed out by Simon in comment 1, is what causes the conversions, even in the array examples in comment 4. Simply put, the compiler prefers to interpret a number as an int, but will try other types if they are a better fit. The only issue is that VRP is used when a type is explicitly specified for an enum with braces.

--
February 15, 2019
https://issues.dlang.org/show_bug.cgi?id=19399

--- Comment #6 from David Eckardt <david.eckardt@frequenz.io> ---
I see two more issues here.

1. For both suffix-less integer literals and `enum name = value` style (i.e. implicit type, no braces) `typeof` yields `int` although they may be coerced into a different type. The same applies for `sizeof` and `alignof. This can cause subtle bugs which are hard to track down (sorry, I am a bit upset because it just happened to me). `typeof`, `sizeof` and the like should be consistent here. As long as they are not, the way of working around type coercion and avoiding bugs is to use `cast(typeof(expression))expression`, which needs an explanation or the next person looking at the code will have a hard time trying to understanding what it is for.

2. Enums and literals of integer arrays such as `[1, 2, 3]` should not match `char[]`. Even `ubyte[]` cannot be implicitly cast to `char[]`.

--
June 27, 2019
https://issues.dlang.org/show_bug.cgi?id=19399

Dlang Bot <dlang-bot@dlang.rocks> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |pull

--- Comment #7 from Dlang Bot <dlang-bot@dlang.rocks> ---
@RazvanN7 created dlang/dmd pull request #10099 "Fix Issues 19399 and 10560 - Different Conversion Rules for Same Value and Type Enum" fixing this issue:

- Fix Issues 19399 and 10560 - Different Conversion Rules for Same Value and Type Enum

https://github.com/dlang/dmd/pull/10099

--
June 26, 2020
https://issues.dlang.org/show_bug.cgi?id=19399

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla@digitalmars.com

--- Comment #8 from Walter Bright <bugzilla@digitalmars.com> ---
Here's how function overload resolution works:

    f(ubyte);
    f(ushort);

    enum E : ushort { a = 10; }

    f(E.a); // which is called?

There are 4 matching levels, 1 is worst and 4 is best:

1. no match
2. implicit conversion
3. conversion to const
4. exact match

E.a can be implicitly converted to a ubyte.
E.a can also be implicitly converted to ushort.

Both are match level 2. To disambiguate this, we now move to "partial ordering". For partial ordering, we ignore what the argument is. We only look at the function parameters. We ask the question "can the parameters of f(ubyte) be used to call the f(ushort)?" The answer is yes, because ubyte can be implicitly converted to ushort. Then we ask "can the parameters of f(ushort) be used to call f(ubyte)?" The answer is no, because ushort cannot be implicitly converted to ubyte.

Therefore, f(ubyte) is "more specialized", and the more specialized function is
selected.

Partial matching is a powerful method, and is far simpler than the C++ method of having a couple pages of match levels which nobody understands. Tellingly, C++ switch from match levels to partial ordering for template overload resolution.

D benefited by this experience and uses partial ordering for both functions and templates.

The compiler is correctly implementing the language semantics.

--
June 26, 2020
https://issues.dlang.org/show_bug.cgi?id=19399

--- Comment #9 from Walter Bright <bugzilla@digitalmars.com> ---
(In reply to Sprink from comment #0)
> void foo(byte v) { writeln("byte ", v); }
> void foo(int v) { writeln("int ", v); }
> 
> enum A : int {
>     a = 127,
>     b = 128, // shh just ignore this
> }
> 
> void main()
> {
>     A v = A.a;
>     foo(A.a);  // byte 127
>     foo(v);    // int 127 should be byte 127
> }

foo(A.a) is passing an integer literal of type enum and value 127. 127 can be
implicitly converted to both byte and int, so foo(byte) and foo(int) both match
with conversion level 2. Partial ordering selects foo(byte).

foo(v) is passing an integer variable of type enum with a base type of int. This cannot be implicitly converted to byte, but it can be implicitly converted to int. Hence, foo(int) is selected. Partial ordering does not come into play. Note that the compiler explicitly does NOT do data flow analysis in the front end to determine that v is 127.

The compiler is working correctly as the language is designed.

--
« First   ‹ Prev
1 2