Skip to content

Commit e2ddcab

Browse files
T-GroCopilot
andcommitted
Fix struct/closure byref capture and let-rec initialization order
Fix 3 codegen bugs in closure generation and initialization: - #19068: Object expressions in struct types generate invalid IL with byref fields - #17692: Duplicate parameter names in closure constructors - #12384: let-rec mutual recursion initialization order for mixed lambda/value bindings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7d0437e commit e2ddcab

18 files changed

Lines changed: 443 additions & 134 deletions

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
* Fix FS3356 false positive for instance extension members with same name on different types, introduced by [#18821](https://github.com/dotnet/fsharp/pull/18821). ([PR #19260](https://github.com/dotnet/fsharp/pull/19260))
2222
* Fix graph-based type checking incorrectly resolving dependencies when the same module name is defined across multiple files in the same namespace. ([PR #19280](https://github.com/dotnet/fsharp/pull/19280))
2323
* F# Scripts: Fix default reference paths resolving when an SDK directory is specified. ([PR #19270](https://github.com/dotnet/fsharp/pull/19270))
24+
* Fix object expressions in struct types no longer generate invalid IL with byref fields. (Issue [#19068](https://github.com/dotnet/fsharp/issues/19068), [PR #19339](https://github.com/dotnet/fsharp/pull/19339))
25+
* Avoid duplicate parameter names in closure constructors. (Issue [#17692](https://github.com/dotnet/fsharp/issues/17692), [PR #19339](https://github.com/dotnet/fsharp/pull/19339))
26+
* Improve let-rec codegen: reorder bindings to allocate lambda closures before non-lambda values that reference them. ([PR #19339](https://github.com/dotnet/fsharp/pull/19339))
2427

2528
### Added
2629
* FSharpType: add ImportILType ([PR #19300](https://github.com/dotnet/fsharp/pull/19300))

src/Compiler/CodeGen/EraseClosures.fs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,21 @@ let mkILFreeVarForParam (p: ILParameter) =
392392

393393
let mkILLocalForFreeVar (p: IlxClosureFreeVar) = mkILLocal p.fvType None
394394

395+
// Note: This is similar to ChooseFreeVarNames in IlxGen.fs but operates on
396+
// IlxClosureFreeVar[] instead of string lists. Kept separate to avoid cross-file dependency.
397+
let mkUniqueFreeVarName (baseName: string) (existingFields: IlxClosureFreeVar[]) =
398+
let existingNames = existingFields |> Array.map (fun fv -> fv.fvName) |> Set.ofArray
399+
400+
let rec findUnique n =
401+
let candidate = if n = 0 then baseName else baseName + string n
402+
403+
if Set.contains candidate existingNames then
404+
findUnique (n + 1)
405+
else
406+
candidate
407+
408+
findUnique 0
409+
395410
let mkILCloFldSpecs _cenv flds =
396411
flds |> Array.map (fun fv -> (fv.fvName, fv.fvType)) |> Array.toList
397412

@@ -490,7 +505,8 @@ let rec convIlxClosureDef cenv encl (td: ILTypeDef) clo =
490505
let laterGenericParams = td.GenericParams @ addedGenParams
491506

492507
let selfFreeVar =
493-
mkILFreeVar (CompilerGeneratedName("self" + string nowFields.Length), true, nowCloSpec.ILType)
508+
let baseName = CompilerGeneratedName("self" + string nowFields.Length)
509+
mkILFreeVar (mkUniqueFreeVarName baseName nowFields, true, nowCloSpec.ILType)
494510

495511
let laterFields = Array.append nowFields [| selfFreeVar |]
496512
let laterCloRef = IlxClosureRef(laterTypeRef, laterStruct, laterFields)
@@ -612,7 +628,8 @@ let rec convIlxClosureDef cenv encl (td: ILTypeDef) clo =
612628
let laterGenericParams = td.GenericParams
613629
// Number each argument left-to-right, adding one to account for the "this" pointer
614630
let selfFreeVar =
615-
mkILFreeVar (CompilerGeneratedName "self", true, nowCloSpec.ILType)
631+
let baseName = CompilerGeneratedName "self"
632+
mkILFreeVar (mkUniqueFreeVarName baseName nowFields, true, nowCloSpec.ILType)
616633

617634
let argToFreeVarMap =
618635
(0, selfFreeVar)

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6450,7 +6450,7 @@ and GenObjectExpr cenv cgbuf eenvouter objExpr (baseType, baseValOpt, basecall,
64506450
GenWitnessArgsFromWitnessInfos cenv cgbuf eenvouter m cloinfo.cloWitnessInfos
64516451

64526452
for fv in cloinfo.cloFreeVars do
6453-
GenGetLocalVal cenv cgbuf eenvouter m fv None
6453+
GenGetFreeVarForClosure cenv cgbuf eenvouter m fv
64546454

64556455
CG.EmitInstr
64566456
cgbuf
@@ -6541,7 +6541,7 @@ and GenSequenceExpr
65416541
if stateVarsSet.Contains fv then
65426542
GenDefaultValue cenv cgbuf eenv (fv.Type, m)
65436543
else
6544-
GenGetLocalVal cenv cgbuf eenv m fv None
6544+
GenGetFreeVarForClosure cenv cgbuf eenv m fv
65456545

65466546
CG.EmitInstr
65476547
cgbuf
@@ -6653,7 +6653,7 @@ and GenSequenceExpr
66536653
if stateVarsSet.Contains fv then
66546654
GenDefaultValue cenv cgbuf eenvouter (fv.Type, m)
66556655
else
6656-
GenGetLocalVal cenv cgbuf eenvouter m fv None
6656+
GenGetFreeVarForClosure cenv cgbuf eenvouter m fv
66576657

66586658
CG.EmitInstr cgbuf (pop ilCloAllFreeVars.Length) (Push [ ilCloRetTyOuter ]) (I_newobj(ilxCloSpec.Constructor, None))
66596659
GenSequel cenv eenvouter.cloc cgbuf sequel
@@ -6886,7 +6886,9 @@ and GenClosureAlloc cenv (cgbuf: CodeGenBuffer) eenv (cloinfo, m) =
68866886
CG.EmitInstr cgbuf (pop 0) (Push [ EraseClosures.mkTyOfLambdas cenv.ilxPubCloEnv cloinfo.ilCloLambdas ]) (mkNormalLdsfld fspec)
68876887
else
68886888
GenWitnessArgsFromWitnessInfos cenv cgbuf eenv m cloinfo.cloWitnessInfos
6889-
GenGetLocalVals cenv cgbuf eenv m cloinfo.cloFreeVars
6889+
6890+
for fv in cloinfo.cloFreeVars do
6891+
GenGetFreeVarForClosure cenv cgbuf eenv m fv
68906892

68916893
CG.EmitInstr
68926894
cgbuf
@@ -6901,6 +6903,12 @@ and GenLambda cenv cgbuf eenv isLocalTypeFunc thisVars expr sequel =
69016903

69026904
and GenTypeOfVal cenv eenv (v: Val) = GenType cenv v.Range eenv.tyenv v.Type
69036905

6906+
and capturedTypeForFreeVar (g: TcGlobals) (fv: Val) =
6907+
if isByrefTy g fv.Type then
6908+
destByrefTy g fv.Type
6909+
else
6910+
fv.Type
6911+
69046912
and GenFreevar cenv m eenvouter tyenvinner (fv: Val) =
69056913
let g = cenv.g
69066914

@@ -6915,7 +6923,7 @@ and GenFreevar cenv m eenvouter tyenvinner (fv: Val) =
69156923
| Method _
69166924
| Null -> error (InternalError("GenFreevar: compiler error: unexpected unrealized value", fv.Range))
69176925
#endif
6918-
| _ -> GenType cenv m tyenvinner fv.Type
6926+
| _ -> GenType cenv m tyenvinner (capturedTypeForFreeVar g fv)
69196927

69206928
and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) boxity eenv takenNames expr =
69216929
let g = cenv.g
@@ -7276,7 +7284,9 @@ and GenDelegateExpr cenv cgbuf eenvouter expr (TObjExprMethod(slotsig, _attribs,
72767284
IlxClosureSpec.Create(IlxClosureRef(ilDelegeeTypeRef, ilCloLambdas, ilCloAllFreeVars), ctxtGenericArgsForDelegee, false)
72777285

72787286
GenWitnessArgsFromWitnessInfos cenv cgbuf eenvouter m cloWitnessInfos
7279-
GenGetLocalVals cenv cgbuf eenvouter m cloFreeVars
7287+
7288+
for fv in cloFreeVars do
7289+
GenGetFreeVarForClosure cenv cgbuf eenvouter m fv
72807290

72817291
CG.EmitInstr
72827292
cgbuf
@@ -8206,6 +8216,17 @@ and GenLetRecFixup cenv cgbuf eenv (ilxCloSpec: IlxClosureSpec, e, ilField: ILFi
82068216
GenExpr cenv cgbuf eenv e2 Continue
82078217
CG.EmitInstr cgbuf (pop 2) Push0 (mkNormalStfld (mkILFieldSpec (ilField.FieldRef, ilxCloSpec.ILType)))
82088218

8219+
and isLambdaBinding (TBind(_, expr, _)) =
8220+
match stripDebugPoints expr with
8221+
| Expr.Lambda _
8222+
| Expr.TyLambda _
8223+
| Expr.Obj _ -> true
8224+
| _ -> false
8225+
8226+
and reorderBindingsLambdasFirst binds =
8227+
let lambdas, nonLambdas = binds |> List.partition isLambdaBinding
8228+
lambdas @ nonLambdas
8229+
82098230
/// Generate letrec bindings
82108231
and GenLetRecBindings cenv (cgbuf: CodeGenBuffer) eenv (allBinds: Bindings, m) (dict: Dictionary<Stamp, ILTypeRef> option) =
82118232

@@ -8299,8 +8320,11 @@ and GenLetRecBindings cenv (cgbuf: CodeGenBuffer) eenv (allBinds: Bindings, m) (
82998320
let recursiveVars =
83008321
Zset.addList (bindsPossiblyRequiringFixup |> List.map (fun v -> v.Var)) (Zset.empty valOrder)
83018322

8323+
let reorderedBindsPossiblyRequiringFixup =
8324+
reorderBindingsLambdasFirst bindsPossiblyRequiringFixup
8325+
83028326
let _ =
8303-
(recursiveVars, bindsPossiblyRequiringFixup)
8327+
(recursiveVars, reorderedBindsPossiblyRequiringFixup)
83048328
||> List.fold (fun forwardReferenceSet (bind: Binding) ->
83058329
// Compute fixups
83068330
bind.Expr
@@ -8344,6 +8368,8 @@ and GenLetRecBindings cenv (cgbuf: CodeGenBuffer) eenv (allBinds: Bindings, m) (
83448368
let _ =
83458369
(recursiveVars, groupBinds)
83468370
||> List.fold (fun forwardReferenceSet (binds: Binding list) ->
8371+
let binds = reorderBindingsLambdasFirst binds
8372+
83478373
match dict, cenv.g.realsig, binds with
83488374
| _, false, _
83498375
| None, _, _
@@ -8380,8 +8406,11 @@ and GenLetRecBindings cenv (cgbuf: CodeGenBuffer) eenv (allBinds: Bindings, m) (
83808406

83818407
and GenLetRec cenv cgbuf eenv (binds, body, m) sequel =
83828408
let _, endMark as scopeMarks = StartLocalScope "letrec" cgbuf
8383-
let eenv = AllocStorageForBinds cenv cgbuf scopeMarks eenv binds
8384-
GenLetRecBindings cenv cgbuf eenv (binds, m) None
8409+
8410+
let reorderedBinds = reorderBindingsLambdasFirst binds
8411+
8412+
let eenv = AllocStorageForBinds cenv cgbuf scopeMarks eenv reorderedBinds
8413+
GenLetRecBindings cenv cgbuf eenv (reorderedBinds, m) None
83858414
GenExpr cenv cgbuf eenv body (EndLocalScope(sequel, endMark))
83868415

83878416
//-------------------------------------------------------------------------
@@ -9850,8 +9879,14 @@ and GenGetStorageAndSequel (cenv: cenv) cgbuf eenv m (ty, ilTy) storage storeSeq
98509879
CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ mkLdarg0; mkNormalLdfld ilField ]
98519880
CommitGetStorageSequel cenv cgbuf eenv m ty localCloInfo storeSequel
98529881

9853-
and GenGetLocalVals cenv cgbuf eenvouter m fvs =
9854-
List.iter (fun v -> GenGetLocalVal cenv cgbuf eenvouter m v None) fvs
9882+
/// Load free variables for closure capture, dereferencing byrefs.
9883+
and GenGetFreeVarForClosure cenv cgbuf eenv m (fv: Val) =
9884+
let g = cenv.g
9885+
GenGetLocalVal cenv cgbuf eenv m fv None
9886+
9887+
if isByrefTy g fv.Type then
9888+
let ilUnderlyingTy = GenType cenv m eenv.tyenv (capturedTypeForFreeVar g fv)
9889+
CG.EmitInstr cgbuf (pop 1) (Push [ ilUnderlyingTy ]) (mkNormalLdobj ilUnderlyingTy)
98559890

98569891
and GenGetLocalVal cenv cgbuf eenv m (vspec: Val) storeSequel =
98579892
GenGetStorageAndSequel cenv cgbuf eenv m (vspec.Type, GenTypeOfVal cenv eenv vspec) (StorageForVal m vspec eenv) storeSequel

0 commit comments

Comments
 (0)