Open brianrourkeboll opened 4 months ago
I honestly don't think we can resolve it easily for general case, since it's a constrained runtime call by design, and it would be hard to distinguish such cases in compile time.
We might though solve this specific one - for type functions, when type inferred is the interface.
I honestly don't think we can resolve it easily for general case, since it's a constrained runtime call by design, and it would be hard to distinguish such cases in compile time.
Yeah I agree, especially since presumably the mechanism that resolves #IFace
→ IFace
for an IWSAM is the same one that resolves, e.g., #('T seq)
→ 'T seq
for a regular interface, which is definitely relied upon.
We might though solve this specific one - for type functions, when type inferred is the interface.
Unfortunately, the same thing happens when it's a regular function, too:
#nowarn "3535"
type IFace =
static abstract P1 : int
type T =
interface IFace with
static member P1 = 1
let f<'T & #IFace> () = 'T.P1 + 'T.P1
f () // 'T is resolved to IFace.
On the other hand, this problem probably shouldn't affect most uses of IWSAMs in the wild, since a type parameter constrained by a (recursively) generic IWSAM (like anything from System.Numerics
) will not silently resolve to the interface itself:
open System.Numerics
let g<'T & #INumber<'T>> () = 'T.Zero + 'T.One
let _ = g ()
// error FS0071: Type constraint mismatch when applying the default type 'INumber<'a>'
// for a type inference variable. The types ''a' and 'INumber<'a>' cannot be unified.
// Consider adding further type constraints
There is no compile-time error when:
A
System.Runtime.AmbiguousImplementationException
is raised at runtime instead. This is raised even when the interface in question has only one static abstract member (or member of any kind).Repro steps
net8.0
net9.0
Expected behavior
Resolution of the type variable should fail at compile-time in the absence of further type information when the type variable has a constraint involving static abstract members — although ideally it would only fail when an
abstract
(as opposed tovirtual
) member was actually being accessed, which is not visible through a constraint like'T & #IFace
alone.Actual behavior
A type variable like
'T & #IFace
resolves to the interface typeIFace
itself, and aSystem.Runtime.AmbiguousImplementationException
is raised at runtime.Known workarounds
Always manually ensure that all type variables are resolved to types with concrete implementations for all static abstract methods that are invoked on them.
Related information
.NET 8/9 preview.
This problem is similar in spirit to #14012, #16299, etc., although it might be a bit trickier to fix.