@@ -2588,6 +2588,10 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs
25882588
25892589 let mutable hasStackAllocatedLocals = false
25902590
2591+ // An uninitialized reference-type 'this', pending for a chained base/self '.ctor', must not be
2592+ // spilled for a debug point: that produces unverifiable IL.
2593+ let mutable uninitializedThisOnStackCount = 0
2594+
25912595 let codeLabelToPC: Dictionary<ILCodeLabel, int> = Dictionary<_, _>(10)
25922596
25932597 let codeLabelToCodeLabel: Dictionary<ILCodeLabel, ILCodeLabel> =
@@ -2629,6 +2633,12 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs
26292633
26302634 member _.GetCurrentStack() = stack
26312635
2636+ member _.StartUninitializedThisOnStack() =
2637+ uninitializedThisOnStackCount <- uninitializedThisOnStackCount + 1
2638+
2639+ member _.EndUninitializedThisOnStack() =
2640+ uninitializedThisOnStackCount <- uninitializedThisOnStackCount - 1
2641+
26322642 member _.AssertEmptyStack() =
26332643 if not (isNil stack) then
26342644 let msg =
@@ -2676,6 +2686,21 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs
26762686 member cgbuf.EmitDebugPoint(m: range) =
26772687 if mgbuf.cenv.options.generateDebugSymbols then
26782688
2689+ // A debug point must be at an empty stack position for the debugger to bind a breakpoint there,
2690+ // so spill anything still pending (e.g. a call argument) to temporaries and reload it afterwards.
2691+ // An uninitialized reference-type 'this' (pending for a chained '.ctor') can't be spilled, so
2692+ // leave the stack as-is in that case.
2693+ let spilled =
2694+ if uninitializedThisOnStackCount > 0 then
2695+ []
2696+ else
2697+ [
2698+ for ty in stack ->
2699+ let idx = cgbuf.AllocLocal([], ty, false, true)
2700+ cgbuf.EmitInstr(pop 1, Push0, mkStloc (uint16 idx))
2701+ idx, ty
2702+ ]
2703+
26792704 let attr = GenILSourceMarker g m
26802705 let i = I_seqpoint attr
26812706 hasDebugPoints <- true
@@ -2696,6 +2721,9 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs
26962721
26972722 anyDocument <- Some attr.Document
26982723
2724+ for idx, ty in List.rev spilled do
2725+ cgbuf.EmitInstr(pop 0, Push [ ty ], mkLdloc (uint16 idx))
2726+
26992727 // Emit FeeFee breakpoints for hidden code, see https://blogs.msdn.microsoft.com/jmstall/2005/06/19/line-hidden-and-0xfeefee-sequence-points/
27002728 member cgbuf.EmitStartOfHiddenCode() =
27012729 if mgbuf.cenv.options.generateDebugSymbols then
@@ -4505,6 +4533,7 @@ and GenApp (cenv: cenv) cgbuf eenv (f, fty, tyargs, curriedArgs, m) sequel =
45054533 List.splitAt numEnclILTypeArgs ilTyArgs
45064534
45074535 let boxity = mspec.DeclaringType.Boxity
4536+ let valu = boxity = AsValue
45084537 let mspec = mkILMethSpec (mspec.MethodRef, boxity, ilEnclArgTys, ilMethArgTys)
45094538
45104539 // "Unit" return types on static methods become "void"
@@ -4560,8 +4589,20 @@ and GenApp (cenv: cenv) cgbuf eenv (f, fty, tyargs, curriedArgs, m) sequel =
45604589 I_call(isTailCall, mspec, None)
45614590
45624591 // ok, now we're ready to generate
4592+ // For a value type the constructor 'this' is a managed pointer, so track it as a byref.
4593+ let thisTy =
4594+ if valu then
4595+ ILType.Byref mspec.DeclaringType
4596+ else
4597+ mspec.DeclaringType
4598+
45634599 if isSuperInit || isSelfInit then
4564- CG.EmitInstr cgbuf (pop 0) (Push [ mspec.DeclaringType ]) mkLdarg0
4600+ CG.EmitInstr cgbuf (pop 0) (Push [ thisTy ]) mkLdarg0
4601+
4602+ let pendingUninitializedThis = (isSuperInit || isSelfInit) && not valu
4603+
4604+ if pendingUninitializedThis then
4605+ cgbuf.StartUninitializedThisOnStack()
45654606
45664607 if not cenv.g.generateWitnesses || witnessInfos.IsEmpty then
45674608 () // no witness args
@@ -4602,9 +4643,12 @@ and GenApp (cenv: cenv) cgbuf eenv (f, fty, tyargs, curriedArgs, m) sequel =
46024643
46034644 CG.EmitInstr cgbuf (pop (nargs + (if mspec.CallingConv.IsStatic || newobj then 0 else 1))) pushes callInstr
46044645
4646+ if pendingUninitializedThis then
4647+ cgbuf.EndUninitializedThisOnStack()
4648+
46054649 // For isSuperInit, load the 'this' pointer as the pretend 'result' of the operation. It will be popped again in most cases
46064650 if isSuperInit then
4607- CG.EmitInstr cgbuf (pop 0) (Push [ mspec.DeclaringType ]) mkLdarg0
4651+ CG.EmitInstr cgbuf (pop 0) (Push [ thisTy ]) mkLdarg0
46084652
46094653 // When generating debug code, generate a 'nop' after a 'call' that returns 'void'
46104654 // This is what C# does, as it allows the call location to be maintained correctly in the stack frame
@@ -5629,10 +5673,21 @@ and GenILCall
56295673 (virt || useCallVirt cenv boxity ilMethSpec isBaseCall)
56305674 && ilMethRef.CallingConv.IsInstance
56315675
5676+ let thisTy =
5677+ if valu then
5678+ ILType.Byref ilMethSpec.DeclaringType
5679+ else
5680+ ilMethSpec.DeclaringType
5681+
56325682 // Load the 'this' pointer to pass to the superclass constructor. This argument is not
56335683 // in the expression tree since it can't be treated like an ordinary value
56345684 if isSuperInit then
5635- CG.EmitInstr cgbuf (pop 0) (Push [ ilMethSpec.DeclaringType ]) mkLdarg0
5685+ CG.EmitInstr cgbuf (pop 0) (Push [ thisTy ]) mkLdarg0
5686+
5687+ let pendingUninitializedThis = isSuperInit && not valu
5688+
5689+ if pendingUninitializedThis then
5690+ cgbuf.StartUninitializedThisOnStack()
56365691
56375692 GenExprs cenv cgbuf eenv argExprs
56385693
@@ -5652,10 +5707,13 @@ and GenILCall
56525707
56535708 CG.EmitInstr cgbuf (pop (argExprs.Length + (if isSuperInit then 1 else 0))) (if isSuperInit then Push0 else Push ilReturnTys) il
56545709
5710+ if pendingUninitializedThis then
5711+ cgbuf.EndUninitializedThisOnStack()
5712+
56555713 // Load the 'this' pointer as the pretend 'result' of the isSuperInit operation.
56565714 // It will be immediately popped in most cases, but may also be used as the target of some "property set" operations.
56575715 if isSuperInit then
5658- CG.EmitInstr cgbuf (pop 0) (Push [ ilMethSpec.DeclaringType ]) mkLdarg0
5716+ CG.EmitInstr cgbuf (pop 0) (Push [ thisTy ]) mkLdarg0
56595717
56605718 CommitCallSequel cenv eenv m eenv.cloc cgbuf mustGenerateUnitAfterCall sequel
56615719
0 commit comments