Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* Fix internal error FS0073 "Undefined or unsolved type variable" in IlxGen when nested inline SRTP functions with multiple overloads leave unsolved typars in the non-witness codegen path. ([Issue #19709](https://github.com/dotnet/fsharp/issues/19709), [PR #19710](https://github.com/dotnet/fsharp/pull/19710))
* Fix NRE when calling virtual Object methods on value types through inline SRTP functions. ([Issue #8098](https://github.com/dotnet/fsharp/issues/8098), [PR #19511](https://github.com/dotnet/fsharp/pull/19511))
* Fix DU case names matching IWSAM member names no longer cause duplicate property entries. (Issue [#14321](https://github.com/dotnet/fsharp/issues/14321), [PR #19341](https://github.com/dotnet/fsharp/pull/19341))
* Fix DefaultAugmentation(false) duplicate entry in method table. (Issue [#16565](https://github.com/dotnet/fsharp/issues/16565), [PR #19341](https://github.com/dotnet/fsharp/pull/19341))
Expand Down
13 changes: 12 additions & 1 deletion src/Compiler/CodeGen/IlxGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,9 @@ type TypeReprEnv
// Random value for post-hoc diagnostic analysis on generated tree *
uint16 666

/// Check if a type parameter is in the environment
member _.ContainsKey(tp: Typar) = reprs.ContainsKey(tp.Stamp)

/// Add an additional type parameter to the environment. If the parameter is a units-of-measure parameter
/// then it is ignored, since it doesn't correspond to a .NET type parameter.
member tyenv.AddOne(tp: Typar) =
Expand Down Expand Up @@ -708,7 +711,15 @@ and GenTypeAux cenv m (tyenv: TypeReprEnv) voidOK ptrsOK ty =
else
EraseClosures.mkILTyFuncTy cenv.ilxPubCloEnv

| TType_var(tp, _) -> mkILTyvarTy tyenv[tp, m]
| TType_var(tp, _) ->
if tyenv.ContainsKey tp then
mkILTyvarTy tyenv[tp, m]
else
// Unsolved type variable not in the TypeReprEnv — can arise for inline SRTP
// functions where constraint resolution leaves phantom typars unsolved.
// Default the typar and generate the type for the default to avoid an ICE.
let defaultTy = TypeRelations.ChooseTyparSolution g cenv.amap tp
GenTypeAux cenv m tyenv voidOK ptrsOK defaultTy

| TType_measure _ -> g.ilg.typ_Int32

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,60 @@ let inline inverse m =
"""
|> typecheck
|> shouldSucceed

[<Fact>]
let ``Nested inline SRTP with multiple overloads should not cause internal error`` () =
// Regression test: unsolved type variables in trait constraint solutions during codegen
// caused FS0073 "internal error: Undefined or unsolved type variable" when an inline
// SRTP function was wrapped in another SRTP dispatch layer with multiple overloads.
FSharp
"""
type App<'F, 'a> = | App of 'F * 'a

type LA = LA
type LB = LB

type D =
static member inline Pur(_witness: App<LA, _>, x: 'a) : App<LA, 'a> = App(LA, x)
static member inline Pur(_witness: App<LB, _>, x: 'a) : App<LB, list<'a>> = App(LB, [x])

let inline pur_impl (_mthd: ^M, output: ^F, x: 'a) : ^F
when (^M or ^F) : (static member Pur : ^F * 'a -> ^F) =
((^M or ^F) : (static member Pur : ^F * 'a -> ^F) (output, x))

let inline pur (x: 'a) : ^F =
pur_impl (Unchecked.defaultof<D>, Unchecked.defaultof< ^F>, x)

type D with
static member inline Invoke(_witness: App<LA, _>, f: App<LA, 'a -> 'b>, x: App<LA, 'a>) : App<LA, 'b> =
let (App(_, fv)) = f
let (App(_, xv)) = x
App(LA, fv xv)
static member inline Invoke(_witness: App<LB, _>, f: App<LB, list<'a -> 'b>>, x: App<LB, list<'a>>) : App<LB, list<'b>> =
let (App(_, fv)) = f
let (App(_, xv)) = x
App(LB, List.map2 (fun f x -> f x) fv xv)

let inline invoke_impl (_mthd: ^M, output: ^R, f: ^FF, x: ^FX) : ^R
when (^M or ^R) : (static member Invoke : ^R * ^FF * ^FX -> ^R) =
((^M or ^R) : (static member Invoke : ^R * ^FF * ^FX -> ^R) (output, f, x))

let inline invoke (f: ^FF) (x: ^FX) : ^R =
invoke_impl (Unchecked.defaultof<D>, Unchecked.defaultof< ^R>, f, x)

[<EntryPoint>]
let main _ =
// Test pur with two overloads (Pur has wildcard _ in App<LA, _>)
let (App(LA, v)) : App<LA, int> = pur 1
if v <> 1 then failwith "pur failed"

// Test invoke with two overloads (Invoke has wildcard _ in App<LA, _>)
let f : App<LA, int -> int> = pur (fun x -> x + 1)
let x : App<LA, int> = pur 2
let (App(LA, r)) : App<LA, int> = invoke f x
if r <> 3 then failwith "invoke failed"
0
"""
|> asExe
|> compileExeAndRun
|> shouldSucceed
Loading