Open gusty opened 5 years ago
This is horrible, thank you very very much @gusty for this reproduction!
Good find and very interesting.
This is a result of the optimizer. For inline functions, if the optimizer sees a conditional where it knows at compile time will only ever be one branch at the call site, it will only output that one branch.
Meaning, in this case, if compiles = false
, it's only outputting Unchecked.defaultof<'mit>
, effectively becoming:
let _ = Unchecked.defaultof<'mit>
T() : T<'mt>
There is probably a bug in the optimizer that causes IlxGen to throw this error which involves a type parameter.
Ok, I see. So, the problem seems to be that at least some part of the type inference is computed after the optimizer pass, which already suppressed the interesting part.
Also, it's interesting to note, that it only shows when I use the static method extensions, with normal let bindings it compiles fine. It's really this specific combination, which happens to be on my plate more often than what it seems.
This is after type inference, so that shouldn't be doing anything.
At a high-level, this is like the flow of the compiler:
Parsing -> TypeChecking -> PostInferenceChecks -> Optimizer -> IlxGen -> Output Assembly
When it's re-writing the expression it might be forgetting to take something into account. The compiler treats methods and let-bound functions as separate entities; means the optimizer has to handle each separately.
Might take a bit to find the solution, but my guess it's something really simple. It's only about finding it.
Seems I'm wrong a bit on this. The issue is more complex than I thought. Hard to tell who the real culprit is.
I have another repro that will give the same internal error regardless of optimizations on or off:
let inline call (output: ^R) : ^a =
((^R or ^M) : (static member Return : ^R -> ^a) output)
type T () =
static member inline Return (x: 'a) : 'b =
call Unchecked.defaultof<'b> x
let inline test () : T =
T.Return(Unchecked.defaultof<T>)
My head is exploding even looking at this smaller example. I know (^R or ^M)
is playing a big factor into this. Going to punt this as I'm not familiar with constraint solver's rules.
I am recording a link to this bug in https://github.com/dotnet/fsharp/pull/6805 to make sure we capture a definitive status for it as part of that work
I will demonstrate a short piece of code, using SRTPs and overload resolution, where the compilation will either succeed or fail, depending on the runtime execution path.
Repro steps
Expected behavior
Should compile or not (hopefully the former) regardless of the content of
compiles
.Actual behavior
It compiles if
compiles = true
but whencompiles = false
it fails witherror FS0073: internal error: Undefined or unsolved type variable: ^_?21440
Known workarounds
Use
compile = true
but it will execute unnecessarily some piece of code that is used only to drive type inference to create the desired constraints.Related information
I've tested it in many environments and different versions of F#. Actually I'm seeing this since long time ago (4 years at least) but it's the first time I manage to create a minimal repro.