1

..and a side of fries.

I have a codebase that I'm compiling for both Windows and MonoTouch. In the wee hours of the morning I coded something like this contrived example which compiles on MonoTouch but fails on Windows:

void Run()
{
    // okay on both
    exec("hello", 1);

    // okay on MonoTouch
    // compiler error on windows
    exec("hello");
}

interface IFace { void foo(); }

void exec(string s, int n=0) 
{ 
    Console.Write("A");  
}
void exec<T>(T t) where T:IFace
{ 
    Console.Write("B");
}

On MonoTouch, this compiles and runs, printing:

AA

On Windows, this example gives a compile-time error:

The type 'string' cannot be used as type parameter 'T' in the generic type or method 'App.Program.exec<T>(T)'. There is no implicit reference conversion from 'string' to 'App.Program.IFace'.

The C# spec on 7.4.2 Overload Resolution says that an 7.4.2.1 Applicable function member must have an identical number of arguments:

The number of arguments in A is identical to the number of parameters in the function member declaration. 7.4.2.1

So it would seem the MonoTouch compiler is considering default arguments when searching for applicable function members, but the Windows compiler is not. So the candidate function members are:

// exec with no default parameters. not applicable because no int supplied
void exec(string,int);

// exec with default value for the second parameter. 
// only considered on MonoTouch.
void exec(string,int=0);

// generic exec with string as the type, which is invalid
// due to the IFace constraint. Invalid on both platforms.
void exec<string>(string) : where T:IFace; 

So, is this a bug in the Applicable Function Member search on MonoTouch, or should the Windows compiler consider the default-parameterized non-generic method as valid?

Cheers, cm

EDIT After dlev's answer, I tested both the constrained and unconstrained generic methods, and it appears the Mono compiler chooses the correct method in the unconstrained case. In the constrained case, it appears that the Mono compiler is either considering the constraint or backtracking to find an alternative when the constraint fails.

The problem/bug reduces to:

void Run()
{
    foo(1);
    bar(1);
}

void foo(int a, int b = 0) { print("A"); }
void foo<T>(T t) { print("B"); }

void bar(int a, int b=0) { print("X"); }
void bar<T>(T t) where T : IFace { print("Y"); }

On both Windows and MonoTouch, foo correctly prints B. But bar fails to compile on Windows yet prints X on MonoTouch.

EDIT2 For those interested, my solution was to remove the default parameters and require explicit invoke. In my actual system, the type constraint specifies two interfaces, so I can't easily change the generic call to exec(IFace t) { ... }. I suppose I could refactor, but this is the meat of my system and the following solves my current compile problems:

void exec(string a) { exec(a,0); }
void exec(string a, int b) { ... }
void exec<T>(T t) where T : IFace, IFace2 { ... }

Double Cheers, cm

cod3monk3y
  • 9,508
  • 6
  • 39
  • 54

1 Answers1

1

This line, from section 7.5.3.2 in the spec suggests that this is a bug in the Mono compiler:

Otherwise if all parameters of MP have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in MQ then MP is better than MQ.

In other words, if you have to substitute a value for an optional parameter to make an applicable method call legal, then that method is considered a worse match then one that does not require such a substitution.

Also, the MS C# compiler does not back-track. Once a method is determined to be the best per the overload resolution rules, compilation continues under that assumption. If later analysis determines that the selected method results in an error (say because the substituted generic argument violates a constraint) then you are told about that error.

And yes, constraints are not part of the signature, so overload resolution does not consider the constraint on T.

dlev
  • 48,024
  • 5
  • 125
  • 132
  • Thanks dlev! That's the part I missed in the spec. I now see how the problem reduces even further to optional parameter overload resolution, as described in detail by Jon Skeet [here](http://stackoverflow.com/questions/2674417/c-sharp-4-conflicting-overloaded-methods-with-optional-parameters) – cod3monk3y Jul 09 '12 at 23:45
  • @cod3monk3y Right. I was just pointing out that you're getting the error because due to the way generic constraints work in C#, overload resolution will be performed without considering their impact. Glad to help! – dlev Jul 09 '12 at 23:48
  • Much appreciated! Now to communicate that to the Mono team! See my edit for the reduced test case. – cod3monk3y Jul 10 '12 at 00:07