dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.91k stars 4.01k forks source link

Overload resolution for float prefers enum over object #33486

Open joncham opened 5 years ago

joncham commented 5 years ago

Version Used: Microsoft (R) Visual C# Compiler version 2.8.2.62916 (2ad4aabc)

Steps to Reproduce:

  1. Compile and run the following test case
class Program
{
    enum MyEnum { Zero }
    static void Main(string[] args) {
        Print(0.0f);
        Print(0.1f);
    }

    static void Print(object a) {
        Console.WriteLine(a);
    }

    static void Print(MyEnum e) {
        Console.WriteLine(e);
    }
}

Expected Behavior: 0 0.1

Actual Behavior: Zero 0.1

Corresponds to Unity issue: https://issuetracker.unity3d.com/issues/gameobject-dot-sendmessage-doesnt-accept-value-as-a-function-argument-correctly-in-the-net-4-dot-x-scripting-runtime

jmarolf commented 5 years ago

@gafter can confirm but I believe this is following specified behavior for the C# language.

There exists an implicit conversion from float to MyEnum and that is more specific than object.

@joncham was this working as you expect in a previous version of the compiler?

mattleibow commented 5 years ago

That is unexpected. Isn't the default enum int-based? So why is there an implicit conversion from float? We can't do a float-to-int implicitly, so why does the enum get to?

chrisoverzero commented 5 years ago

It’s my understanding that this was added to csc.exe purposefully by Eric Lippert back in 2006.

If there’s any desire to change it back, I imagine there are monumental back-compat concerns, even if the premature optimization mentioned in the related blog posts has been removed.

@mattleibow There’s an implicit conversion from a literal zero, which the compiler sometimes (has in the past, at least) works quite hard at deducing.

ericlippert commented 5 years ago

Chrisoverzero's comment above is correct; thanks Chris.

This is probably the most embarrassing of my many compiler bugs, and one of my earlier C# compiler bugs.

You can read the whole story here: see Jon's answer and my answer:

https://stackoverflow.com/questions/23762475/

The history of the bug that motivated me to make the bad change in the first place is here:

https://blogs.msdn.microsoft.com/ericlippert/2006/03/28/the-root-of-all-evil-part-one/

https://blogs.msdn.microsoft.com/ericlippert/2006/03/29/the-root-of-all-evil-part-two/

We decided back in 2006 to stop making unnecessary breaking changes and maintain compatibility with the existing implementation, even though it means that a variable of type enum can be assigned 0.0 without a cast, but one of type int cannot.

gafter commented 5 years ago

This is one place where we might be able to add a warning (in a warning wave) when you accidentally tickle this bug.

agocke commented 5 years ago

Yup, agreed that this issue is by design.

gafter commented 5 years ago

Reopening to track adding a warning for this situation.