2

I've noticed odd compiler specific enforcement of overload resolution after pulling in the code on a Xamarin project that was being developed exclusively on OSX with Xamarin Studio into a Win 10 box running Visual Studio 2015.2. My curiosity eventually lead me to this post which had the benefit of the attentions of Jon Skeet and Eric Lippert, and was really informative on the subject.

That said, I wanted to play around to see what a minimum replication story would be for this difference between the two compilers, and what I've managed to do is create the following two snippets.

I'll start this off with the Roslyn success case: ​

using System;

public static class Program
{
    public static void Main() { Foo(Bar); } // Outputs Func

    public static void Bar(string input) { Console.WriteLine(input); }
    public static string Bar() { return string.Empty; }

    public static void Foo(Action<string> input) { input("Action"); }
    public static void Foo(Func<string> input) { Console.WriteLine("Func"); }
}

And the case in which Mono will work:

using System;

public static class Program
{
    public static void Main() { Foo(Bar); }

    public static void Bar() { Console.WriteLine("Action"); }

    public static void Foo(Action input) { input(); }
    public static void Foo(Func<string> input) { Console.WriteLine("Func"); }
}

Final recap on compiler versions used:

Roslyn: 1.2.0.60425

v12: 12.0.31010.0

mono: 4.2.3.0

So I'm out of my depth at this point, and my understanding is that the specification is a bit murky about this particular part of how things are handled. Having code that is specific to a compiler to be valid is obviously not the best, so I may just need to request that team members either provide a lambda expression to avoid ambiguity over Method Groups, or at the least an explicit cast.

The Roslyn case is especially odd to me, as it seems that it's decision is the most arbitrary of the selections. Any additional insight would be greatly appreciated.

Edit:

I did manage to find an additional snippet that will successfully compile on both Mono and Roslyn but fail on the v12 compiler:

using System;

public static class Program
{
    public static void Main() { Foo(Bar); }

    public static string Bar() { return string.Empty; }

    public static void Foo(Action bar) { Console.WriteLine("BAR"); }
    public static void Foo(Func<string> input) { Console.WriteLine("Func"); }
}
Community
  • 1
  • 1
Jonathon Chase
  • 9,396
  • 21
  • 39
  • what version of mono are you using? – knocte May 28 '16 at 05:48
  • 4.5 x86, mcs shows 4.0.30319.17020 – Jonathon Chase May 28 '16 at 06:12
  • that's not the Mono version, that's the profile version (4.5) and the runtime version (4.0.30319....). you have to check via `mono --version` – knocte May 28 '16 at 07:25
  • That just reports the version of the jitter, which is 4.2.3 – Jonathon Chase May 28 '16 at 16:21
  • no, that is the mono version – knocte May 28 '16 at 16:26
  • PS C:\Program Files (x86)\Mono\bin> .\mono.exe --version Mono JIT compiler version 4.2.3 (Visual Studio built mono) – Jonathon Chase May 28 '16 at 16:42
  • You're right though, the ambiguous wording threw me off. – Jonathon Chase May 28 '16 at 16:45
  • since mono 4.2.3 is modern enough, you should report your problem to http://bugzilla.xamarin.com/ ; stackoverflow is for questions, not for filing bugs ;) – knocte May 28 '16 at 16:58
  • I can't confirm whether or not it is a bug in Mono, a bug in Roslyn, or an issue with the specification, or an issue with respective implementations. Mono's behavior, to me, seems to be the more clear implementation. – Jonathon Chase May 28 '16 at 17:14
  • 1
    yeah right, but in most cases Mono wants to follow what MS.NET does, even if the latter is a bit buggy or its behaviour is out of spec, so please file the differing behaviour as a bug in http://bugzilla.xamarin.com (now that xamarin is part of MS, they may actually go and fix it in roslyn, but they need to know first) – knocte May 30 '16 at 06:02
  • You've certainly done some good spec diving here, but I am at a loss to know what your question is that you would like answered. – Eric Lippert May 30 '16 at 06:19
  • At this point, I don't even know anymore. I started out wondering 'Is this all wrong, or am I crazy?' and the answer turned out to be yes. I think for the time being I'm just going to have to accept that Xamarin Studio+mono =/= VS2015+Roslyn and setup environments accordingly. – Jonathon Chase May 30 '16 at 06:43
  • @knocte Good point, done and done. – Jonathon Chase May 30 '16 at 10:59
  • if by done you mean you've filed the bug, it's good if you put the URL here for reference (in case other people come across this page) – knocte May 30 '16 at 11:08

1 Answers1

2

Okay, I walked away from this for a while, worked on some other things, simplified the usages a little bit more, and I think I've determined the following, with the help of finding this in the specifications.

7.5.3.5 Better conversion target

Given two different types T1 and T2, T1 is a better conversion target than T2 if

  • An implicit conversion from T1 to T2 exists

  • T1 is either a delegate type D1 or an expression tree type Expression, T2 is either a delegate type D2 or an expression tree type Expression, D1 has a return type S1 and one of the following holds:

    • D2 is void returning

    • D2 has a return type S2, and S1 is a better conversion target than S2

The issue here seems to be that, given the same number of parameters between competing methods in a group, a non-void return type will always be preferred. So given Eric Lippert's example from the aforementioned, what was once a compiler error is now acceptable, assuming it as a single, non-void return type that perfectly matches the expected incoming method group. The odd thing here, though, is even if the void method in the group is the only logical match for the target method's accepted parameters, the method with the return type will be preferred and fail to compile.

So Method groups do seem to consider the return type for selection under Roslyn, so long as, given equal input parameters, only one of the possible members of the method group have a return type other than void

//Fine in Roslyn, not so much in Mono
using System;

public static class Program
{
    public static void Main() { Foo(Bar); } // Outputs Func

    public static string Bar() { return string.Empty; }
    public static void Bar(string s1, string s2, string s3) { }
    public static void Bar(string s1, string s2) {}
    public static void Bar(string s1) { }

    //untick for compile failure
    //public static void Foo(Func<int> input) { }
    public static void Foo(Action<string, string, string> input) { }
    public static void Foo(Action<string, string> input) { }
    public static void Foo(Action<string> input) { }
    public static void Foo(Func<string> input) { Console.WriteLine("Func"); }
}

While Mono seems to have gone in the opposite direction, instead considering the return type to be part of the selection process, which seems to imply that the mono implementation of §7.6.5.1 is different than Roslyns.

//Fine in mono, not so much in Rosyln
using System;

public static class Program
{
    public static void Main() { Foo(Bar); }  // Outputs Func<string>

    public static string Bar(string s1, string s2, string s3) { return "Fail"; }
    public static string Bar(string s1, string s2) { return "Fail"; }
    public static string Bar(string s1) { return "Fail"; }
    public static string Bar() { return "Pass"; }

    // untick for compile failure
    // public static void Foo(Func<string,string,string,string> input) { Console.WriteLine("<string,string,string,string>"); }
    public static void Foo(Func<string> input) { Console.WriteLine("Func<string>"); }
    public static void Foo(Func<int> input) { Console.WriteLine("Func<int>"); }
    public static void Foo(Func<decimal> input) { Console.WriteLine("Func<decimal>"); }
    public static void Foo(Func<char> input) {Console.WriteLine("Func<char>");}
}

And neither will work in the previous compiler version, for the reasons Eric Lippert previously outlined. This was informative to me, at least, although I don't really know what I've learned. I guess don't overload method groups, and if you do, make sure it plays nice with both compilers.

Community
  • 1
  • 1
Jonathon Chase
  • 9,396
  • 21
  • 39