ironpython2 icon indicating copy to clipboard operation
ironpython2 copied to clipboard

Referencing multiple assemblies can trigger TypeError when calling an extension method

Open slozier opened this issue 2 years ago • 19 comments

Running the following in .NET (Core):

import clr
clr.AddReference("System.Core")
clr.AddReference("System.Linq")
import System.Linq
clr.ImportExtensions(System.Linq)
range(1,10).ElementAt(5)

leads to the following TypeError:

TypeError: Multiple targets could match: ElementAt(IEnumerable[Int32], Int32), ElementAt(IEnumerable[Int32], Int32)

Reported by @Simon900225 on gitter.

slozier avatar Mar 02 '22 20:03 slozier

Interestingly, it works fine in ipy3.

BCSharp avatar Mar 02 '22 20:03 BCSharp

Sorry, false alarm , I see label "core" now.

BCSharp avatar Mar 02 '22 20:03 BCSharp

It seems that on .NET Core, Enumerable extensions are exported by System.Core.dll, and by System.Linq.dll. Therefore there are two identical ambiguous targets, each one comes from a different assembly. The above example works on .NET Core if one of the DLL references is removed. It also keeps working on .NET Framework if the reference to System.Linq (but not System.Core) is removed.

I am not sure what should be a desired behaviour here; should overloads from one DLL hide extension methods of matching signature defined in another DLL? Any order of priority?

BCSharp avatar Mar 03 '22 01:03 BCSharp

My initial reaction to the issue is that if the user was loading two different versions of an assembly I would just say fix your code. But in this case they're loading System.Core which is just a facade assembly that's type-forwarding Enumerable to the System.Linq assembly so it's actually the same type? Feels like we should be able to avoid the ambiguity in this case.

slozier avatar Mar 03 '22 15:03 slozier

@slozier I think I agree with you: if two or more assemblies export extension methods with the same signature, then it is a legitimate "ambiguous call" case and the error message is warranted (I wish it were more informative, though). However, if it just a case of a simple type forwarding, then ImportExtensions should avoid importing from the same type twice.

BCSharp avatar Mar 04 '22 20:03 BCSharp

A sidenote is that it worked up until 2.7.9 and then stopped working from 2.7.10 and forward.

Simon900225 avatar Mar 07 '22 08:03 Simon900225

@Simon900225 Were you using 2.7.9 with .NET Core?

slozier avatar Mar 07 '22 14:03 slozier

Yes, with .NET 6. But I guess that it's running with help of the "compatibility layer" then as 2.7.9 is not built with .NET core as a target.

Simon900225 avatar Mar 07 '22 15:03 Simon900225

Ok, 2.7.9 has a .NET Core 2.1 target so I assume .NET 6 will use those assemblies. I'll have to see what changed between 2.7.9 and 2.7.10.

slozier avatar Mar 07 '22 15:03 slozier

Looks like it started failing on https://github.com/IronLanguages/ironpython2/pull/619 (which makes sense).

slozier avatar Mar 12 '22 18:03 slozier

Similar issue hitting slightly different code paths:

import clr
clr.AddReference("System.Core")
import System.Linq
clr.ImportExtensions(System.Linq)
clr.ImportExtensions(System.Linq.Enumerable)
range(1,10).ElementAt(5)

In this case the 2nd ImportExtensions is loading the System.Linq assembly (because that's the assembly of the forwarded type) and we then get the same failure.

slozier avatar Mar 12 '22 19:03 slozier

Looks like test_cliclass.test_extension_methods is hitting this issue.

slozier avatar Apr 15 '22 16:04 slozier