Compiler resolves to wrong overloaded constructor or method

  • Thread starter Thread starter Philippe Bertrand
  • Start date Start date
Mike Schilling said:
0.0 is a double both before and after the cast. The fact that they act
differently is, to me, the strangest part.

It is odd, but the compiler probably converts 0.0 to the literal 0. It may
not be spec'd as such(I havn't checked teh fine print), but it is an
understandable issue consider 0.0 and 0 are effectivly the same thing.

However, by issuing a cast you probably get something like
CastExpression(double,LiteralExpression(0)) in the parse tree, instead of
LiteralExpression(0).

As to why (int) wouldn't do the same thing is beyond me, perhaps (int) casts
are discarded on literals.
 
Daniel O'Connell said:
It is odd, but the compiler probably converts 0.0 to the literal 0. It may
not be spec'd as such(I havn't checked teh fine print), but it is an
understandable issue consider 0.0 and 0 are effectivly the same thing.

They're not the same in any other context I know of, though: they're of
different types. For example:

int i = 0.0;

leads to the error

error CS0029: Cannot implicitly convert type 'double' to 'int'

Entirely correct, since such a cast can lose information, even though it
doesn't for this value.

However, by issuing a cast you probably get something like
CastExpression(double,LiteralExpression(0)) in the parse tree, instead of
LiteralExpression(0).

As to why (int) wouldn't do the same thing is beyond me, perhaps (int) casts
are discarded on literals.

I think we agree here that the obsreved behavior seems to fall out of the
compiler implemenation, rather than being derivable from the language
definition..
 
They're not the same in any other context I know of, though: they're of
different types. For example:

int i = 0.0;

leads to the error

error CS0029: Cannot implicitly convert type 'double' to 'int'

Entirely correct, since such a cast can lose information, even though it
doesn't for this value.

I agree, however I do suspect that 0 is defined the same or similarly. My
guess would be that the Enum cast doesn't check type, instead it simply
looks for a literal valued to 0. Whats more intresting, does something like
0.0f or 0l result in the enum overload being called?
I think we agree here that the obsreved behavior seems to fall out of the
compiler implemenation, rather than being derivable from the language
definition..

Yes, I don't *think* there is anything in the spec that defines 0.0 as
implicitly convertible to an enum, however I havn't sat down and traced the
spec entirely, so I can't state that the spec doesn't define this.

However, in the spec or not, it is now effectivly a semantically required
portion of the language. Microsoft can't really change its compiler and
third party compiler vendors would have to support this in either case,
removing it will break code, unfortunatly.
 
Daniel O'Connell said:
I agree, however I do suspect that 0 is defined the same or similarly. My
guess would be that the Enum cast doesn't check type, instead it simply
looks for a literal valued to 0. Whats more intresting, does something
like 0.0f or 0l result in the enum overload being called?

Good question. What the C# spec says is:

13.1.3 Implicit enumeration conversions
An implicit enumeration conversion permits the decimal-integer-literal 0
to be converted to any enum-type.

According to 9.4.4.2, the decimal-integer-literal 0 can be

0, possibly suffixed by one of U u L l UL Ul uL ul LU Lu lU lu
0x0, possilby suffixed the same way

or of course 00, 0x00, 000, 0x000, etc, again possibly suffixed. And I
think this is exactly what was intended. It's not that any integer values
are implicitly converted to enums. It's that there is a special constant
that's a member of all enum types, it means "no flags set", and it's
spelled "0".

As far as I can see, that's the only place the implicit conversion should
apply, so, in theory, 0l yes, 0f no.
Yes, I don't *think* there is anything in the spec that defines 0.0 as
implicitly convertible to an enum, however I havn't sat down and traced
the spec entirely, so I can't state that the spec doesn't define this.

I did some searching for "conversion", "cast:", etc. and didn't find
anything suggestive-looking.
However, in the spec or not, it is now effectivly a semantically required
portion of the language. Microsoft can't really change its compiler and
third party compiler vendors would have to support this in either case,
removing it will break code, unfortunatly.

There are ways to address this, e.g:

Version N+1 of the compiler issues a warning that the usage is
deprecated and will cause an error in future releases
Version N+2 issues an error unless a special compatibility-mode flag is
set
Version N+3 issues an error, period

But this is a small enough point that, I agree, there's no real point in
making the compiler stricter.
 
Daniel O'Connell said:
On that I have *no* idea. I think the enum structure simply uses the first
defined, but I could be wrong on that.
I get a different order if I'm making an assembly for the Compact Framework
than if I'm making it for the regular framework.

I also noticed that out of 4 pairs of enumerations with same numeric value,
in the .NET Framework only in 3 pairs, the ToString() output is the first
one defined!

Philippe
 
There is no magic. The language designers wanted to avoid
constructs like "(SomeEnum)0", because enums may be
[Flags]-able, and thus they may be "0" w/out having
a literal with the value "0".

Consider this:

if ((val & SomeEnum.Foo) == 0) {
}

W/out this rule, the bool expression has to
be written like:

if ((val & SomeEnum.Foo) == (SomeEnum)0) {
}


Why not a keyword "empty" which can implicitly cast to every enum type or
maybe also to every value type since every value type ahs a well defined
state in which every bit is zeroed out (which is what the default ctor of
every struct does)

os following could be possible:

MyStruct s = empty;
Point p = empty;
int i = empty;

if ((val & SomeEnum.Foo) == empty) {}

maybe it could also used in generics instread of T.default.
 
cody said:
There is no magic. The language designers wanted to avoid
constructs like "(SomeEnum)0", because enums may be
[Flags]-able, and thus they may be "0" w/out having
a literal with the value "0".

Consider this:

if ((val & SomeEnum.Foo) == 0) {
}

W/out this rule, the bool expression has to
be written like:

if ((val & SomeEnum.Foo) == (SomeEnum)0) {
}


Why not a keyword "empty" which can implicitly cast to every enum type or
maybe also to every value type since every value type ahs a well defined
state in which every bit is zeroed out (which is what the default ctor of
every struct does)

os following could be possible:

MyStruct s = empty;
Point p = empty;
int i = empty;

if ((val & SomeEnum.Foo) == empty) {}

maybe it could also used in generics instread of T.default.

Well, default is already set(default(T) now), and may be a better solution
for an Enum at this point(I havn't tested to see if it works, but it is much
nicer than implicit 0 conversion).
Also, I think it would be better to simply have:
MyStruct s = default;
rather than defining a new keyword.
 
Daniel O'Connell said:
cody said:
There is no magic. The language designers wanted to avoid
constructs like "(SomeEnum)0", because enums may be
[Flags]-able, and thus they may be "0" w/out having
a literal with the value "0".

Consider this:

if ((val & SomeEnum.Foo) == 0) {
}

W/out this rule, the bool expression has to
be written like:

if ((val & SomeEnum.Foo) == (SomeEnum)0) {
}


Why not a keyword "empty" which can implicitly cast to every enum type or
maybe also to every value type since every value type ahs a well defined
state in which every bit is zeroed out (which is what the default ctor of
every struct does)

os following could be possible:

MyStruct s = empty;
Point p = empty;
int i = empty;

if ((val & SomeEnum.Foo) == empty) {}

maybe it could also used in generics instread of T.default.

Well, default is already set(default(T) now), and may be a better solution
for an Enum at this point(I havn't tested to see if it works, but it is much
nicer than implicit 0 conversion).
Also, I think it would be better to simply have:
MyStruct s = default;
rather than defining a new keyword.

It looks very strange but it would do the job :)
 
hi cody,
There is no magic. The language designers wanted to avoid
constructs like "(SomeEnum)0", because enums may be
[Flags]-able, and thus they may be "0" w/out having
a literal with the value "0".

Consider this:

if ((val & SomeEnum.Foo) == 0) {
}

W/out this rule, the bool expression has to
be written like:

if ((val & SomeEnum.Foo) == (SomeEnum)0) {
}


Why not a keyword "empty" which can implicitly cast to every enum type
or
maybe also to every value type since every value type ahs a well defined
state in which every bit is zeroed out (which is what the default ctor
of
every struct does)

os following could be possible:

MyStruct s = empty;
Point p = empty;
int i = empty;

if ((val & SomeEnum.Foo) == empty) {}

maybe it could also used in generics instread of T.default.

Well, default is already set(default(T) now), and may be a better solution
for an Enum at this point(I havn't tested to see if it works, but it is
much

nicer than implicit 0 conversion).
Also, I think it would be better to simply have:
MyStruct s = default;
rather than defining a new keyword.


It looks very strange but it would do the job :)

I'd vote for "empty" ;-)

Consider this not quite uncommon enum usage pattern:

switch (someEnumVarWithBrandNewDefaultSemantics)
{
case TotallyWeird:
return foo;

case default:
return bar;

default:
return idonttakeit;
}

That's ugly. Languages have to be nice, consistent and orthogonal.
I fully understand why Anders went for "0" ;-)

bye
Rob
 
Robert Jordan said:
hi cody,
There is no magic. The language designers wanted to avoid
constructs like "(SomeEnum)0", because enums may be
[Flags]-able, and thus they may be "0" w/out having
a literal with the value "0".

Consider this:

if ((val & SomeEnum.Foo) == 0) {
}

W/out this rule, the bool expression has to
be written like:

if ((val & SomeEnum.Foo) == (SomeEnum)0) {
}


Why not a keyword "empty" which can implicitly cast to every enum type
or

maybe also to every value type since every value type ahs a well defined
state in which every bit is zeroed out (which is what the default ctor
of

every struct does)

os following could be possible:

MyStruct s = empty;
Point p = empty;
int i = empty;

if ((val & SomeEnum.Foo) == empty) {}

maybe it could also used in generics instread of T.default.


Well, default is already set(default(T) now), and may be a better
solution
for an Enum at this point(I havn't tested to see if it works, but it is
much

nicer than implicit 0 conversion).
Also, I think it would be better to simply have:
MyStruct s = default;
rather than defining a new keyword.


It looks very strange but it would do the job :)

I'd vote for "empty" ;-)

I wouldn't. New keyword is bad mojo, especially for something as minor as
this. I am happy with 0 myself, ;).
 
There is no magic. The language designers wanted to avoid
constructs like "(SomeEnum)0", because enums may be
[Flags]-able, and thus they may be "0" w/out having
a literal with the value "0".

Consider this:

if ((val & SomeEnum.Foo) == 0) {
}

W/out this rule, the bool expression has to
be written like:

if ((val & SomeEnum.Foo) == (SomeEnum)0) {
}


Why not a keyword "empty" which can implicitly cast to every enum type
or

maybe also to every value type since every value type ahs a well defined
state in which every bit is zeroed out (which is what the default ctor
of

every struct does)

os following could be possible:

MyStruct s = empty;
Point p = empty;
int i = empty;

if ((val & SomeEnum.Foo) == empty) {}

maybe it could also used in generics instread of T.default.


Well, default is already set(default(T) now), and may be a better solution
for an Enum at this point(I havn't tested to see if it works, but it is
much

nicer than implicit 0 conversion).
Also, I think it would be better to simply have:
MyStruct s = default;
rather than defining a new keyword.


It looks very strange but it would do the job :)

I'd vote for "empty" ;-)

Consider this not quite uncommon enum usage pattern:

switch (someEnumVarWithBrandNewDefaultSemantics)
{
case TotallyWeird:
return foo;

case default:
return bar;

default:
return idonttakeit;
}


This is indeed very dangerous, it would lead to subtly bug. I sometimes
inadvertantly write case default: instead of default: which would compile
fine but doing some different things.
So I'd also vote for "empty" again :)
 
Back
Top