Skip to content

Commit b2c9e0b

Browse files
authored
Fix internal error FS0073 w/nested inline SRTP and multiple overloads (#19710)
* Fix FS0073 ICE for unsolved typars in non-witness IlxGen codepath * Adjust comment
1 parent 34151e9 commit b2c9e0b

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### Fixed
22

3+
* 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))
34
* 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))
45
* 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))
56
* 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))

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,9 @@ type TypeReprEnv
529529
// Random value for post-hoc diagnostic analysis on generated tree *
530530
uint16 666
531531

532+
/// Check if a type parameter is in the environment
533+
member _.ContainsKey(tp: Typar) = reprs.ContainsKey(tp.Stamp)
534+
532535
/// Add an additional type parameter to the environment. If the parameter is a units-of-measure parameter
533536
/// then it is ignored, since it doesn't correspond to a .NET type parameter.
534537
member tyenv.AddOne(tp: Typar) =
@@ -708,7 +711,15 @@ and GenTypeAux cenv m (tyenv: TypeReprEnv) voidOK ptrsOK ty =
708711
else
709712
EraseClosures.mkILTyFuncTy cenv.ilxPubCloEnv
710713

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

713724
| TType_measure _ -> g.ilg.typ_Int32
714725

tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,60 @@ let inline inverse m =
239239
"""
240240
|> typecheck
241241
|> shouldSucceed
242+
243+
[<Fact>]
244+
let ``Nested inline SRTP with multiple overloads should not cause internal error`` () =
245+
// Regression test: unsolved type variables in trait constraint solutions during codegen
246+
// caused FS0073 "internal error: Undefined or unsolved type variable" when an inline
247+
// SRTP function was wrapped in another SRTP dispatch layer with multiple overloads.
248+
FSharp
249+
"""
250+
type App<'F, 'a> = | App of 'F * 'a
251+
252+
type LA = LA
253+
type LB = LB
254+
255+
type D =
256+
static member inline Pur(_witness: App<LA, _>, x: 'a) : App<LA, 'a> = App(LA, x)
257+
static member inline Pur(_witness: App<LB, _>, x: 'a) : App<LB, list<'a>> = App(LB, [x])
258+
259+
let inline pur_impl (_mthd: ^M, output: ^F, x: 'a) : ^F
260+
when (^M or ^F) : (static member Pur : ^F * 'a -> ^F) =
261+
((^M or ^F) : (static member Pur : ^F * 'a -> ^F) (output, x))
262+
263+
let inline pur (x: 'a) : ^F =
264+
pur_impl (Unchecked.defaultof<D>, Unchecked.defaultof< ^F>, x)
265+
266+
type D with
267+
static member inline Invoke(_witness: App<LA, _>, f: App<LA, 'a -> 'b>, x: App<LA, 'a>) : App<LA, 'b> =
268+
let (App(_, fv)) = f
269+
let (App(_, xv)) = x
270+
App(LA, fv xv)
271+
static member inline Invoke(_witness: App<LB, _>, f: App<LB, list<'a -> 'b>>, x: App<LB, list<'a>>) : App<LB, list<'b>> =
272+
let (App(_, fv)) = f
273+
let (App(_, xv)) = x
274+
App(LB, List.map2 (fun f x -> f x) fv xv)
275+
276+
let inline invoke_impl (_mthd: ^M, output: ^R, f: ^FF, x: ^FX) : ^R
277+
when (^M or ^R) : (static member Invoke : ^R * ^FF * ^FX -> ^R) =
278+
((^M or ^R) : (static member Invoke : ^R * ^FF * ^FX -> ^R) (output, f, x))
279+
280+
let inline invoke (f: ^FF) (x: ^FX) : ^R =
281+
invoke_impl (Unchecked.defaultof<D>, Unchecked.defaultof< ^R>, f, x)
282+
283+
[<EntryPoint>]
284+
let main _ =
285+
// Test pur with two overloads (Pur has wildcard _ in App<LA, _>)
286+
let (App(LA, v)) : App<LA, int> = pur 1
287+
if v <> 1 then failwith "pur failed"
288+
289+
// Test invoke with two overloads (Invoke has wildcard _ in App<LA, _>)
290+
let f : App<LA, int -> int> = pur (fun x -> x + 1)
291+
let x : App<LA, int> = pur 2
292+
let (App(LA, r)) : App<LA, int> = invoke f x
293+
if r <> 3 then failwith "invoke failed"
294+
0
295+
"""
296+
|> asExe
297+
|> compileExeAndRun
298+
|> shouldSucceed

0 commit comments

Comments
 (0)