From ad270ae08eaab09237a4774a9cd5ad0e4f08e27d Mon Sep 17 00:00:00 2001 From: Nick Nassiri Date: Mon, 15 Jun 2026 20:15:37 -0700 Subject: [PATCH] Remove dead LoadVariable method from AsyncArrowMoveNextEmitter `LoadVariable(string)` in AsyncArrowMoveNextEmitter.Variables.cs had no callers. The only live variable-load paths in this emitter are the `EmitVariable` override and `LoadVariableForCapture` (used by ArrowFunctions.cs); `LoadVariable` was a near-duplicate of `EmitVariable` left behind. It was also a latent copy of the bug fixed in #648 / PR #671: like the pre-fix `EmitVariable`, its `Ldnull` fallback never consulted the JS global constants (NaN/Infinity/undefined), so had it ever been wired up a bare `NaN`/`Infinity` would again compile to a null load. Delete the dead method and update the two comments that referenced it by name so they point at the live `EmitVariable` override / capture-population loader instead. No behavioral change. Build clean; AsyncNaNStrictEqualityTests 20/20 green. --- ...syncArrowMoveNextEmitter.ArrowFunctions.cs | 2 +- .../AsyncArrowMoveNextEmitter.Variables.cs | 85 +------------------ 2 files changed, 2 insertions(+), 85 deletions(-) diff --git a/Compilation/AsyncArrowMoveNextEmitter.ArrowFunctions.cs b/Compilation/AsyncArrowMoveNextEmitter.ArrowFunctions.cs index 1fdba6e8..ae62a051 100644 --- a/Compilation/AsyncArrowMoveNextEmitter.ArrowFunctions.cs +++ b/Compilation/AsyncArrowMoveNextEmitter.ArrowFunctions.cs @@ -66,7 +66,7 @@ private void EmitCapturingArrowInAsyncArrow(Expr.ArrowFunction af, MethodBuilder { _il.Emit(OpCodes.Dup); // Keep display class instance on stack - // Load the captured variable using the same logic as LoadVariable + // Load the captured variable using the capture-population loader LoadVariableForCapture(capturedVar); _il.Emit(OpCodes.Stfld, field); diff --git a/Compilation/AsyncArrowMoveNextEmitter.Variables.cs b/Compilation/AsyncArrowMoveNextEmitter.Variables.cs index d85afae5..857d1c3d 100644 --- a/Compilation/AsyncArrowMoveNextEmitter.Variables.cs +++ b/Compilation/AsyncArrowMoveNextEmitter.Variables.cs @@ -140,89 +140,6 @@ protected override void EmitAssign(Expr.Assign a) protected override void EmitStoreVariable(string name) => StoreVariable(name); - private void LoadVariable(string name) - { - // Check if it's a parameter of this arrow - if (_builder.ParameterFields.TryGetValue(name, out var paramField)) - { - _il.Emit(OpCodes.Ldarg_0); - _il.Emit(OpCodes.Ldfld, paramField); - SetStackUnknown(); - return; - } - - // Check if it's a hoisted local of this arrow - if (_builder.LocalFields.TryGetValue(name, out var localField)) - { - _il.Emit(OpCodes.Ldarg_0); - _il.Emit(OpCodes.Ldfld, localField); - SetStackUnknown(); - return; - } - - // Check if it's captured from outer scope - if (_builder.IsCaptured(name) && _builder.CapturedFieldMap.TryGetValue(name, out var outerField)) - { - // Load through outer reference - // Use Unbox (not Unbox_Any) to get a pointer to the boxed struct, then load field - _il.Emit(OpCodes.Ldarg_0); - _il.Emit(OpCodes.Ldfld, _builder.OuterStateMachineField!); - - // Check if this is a transitive capture (needs extra indirection through parent's outer) - if (_builder.TransitiveCaptures.Contains(name) && - _builder.ParentOuterStateMachineField != null && - _builder.GrandparentStateMachineType != null) - { - // First unbox to parent, then load parent's outer reference - _il.Emit(OpCodes.Unbox, _builder.OuterStateMachineType!); - _il.Emit(OpCodes.Ldfld, _builder.ParentOuterStateMachineField); - _il.Emit(OpCodes.Unbox, _builder.GrandparentStateMachineType); - } - else - { - _il.Emit(OpCodes.Unbox, _builder.OuterStateMachineType!); - } - - _il.Emit(OpCodes.Ldfld, outerField); - SetStackUnknown(); - return; - } - - // Check for non-hoisted local variable - if (_locals.TryGetValue(name, out var local)) - { - _il.Emit(OpCodes.Ldloc, local); - SetStackUnknown(); - return; - } - - // Check if it's an imported value (from another module) - must check BEFORE Functions - // because cross-module function references need to go through the import field - if (_ctx?.TopLevelStaticVars?.TryGetValue(name, out var topLevelField) == true) - { - _il.Emit(OpCodes.Ldsfld, topLevelField); - SetStackUnknown(); - return; - } - - // Check if it's a global function - if (_ctx?.Functions.TryGetValue(_ctx.ResolveFunctionName(name), out var funcMethod) == true) - { - // Load function reference - _il.Emit(OpCodes.Ldnull); - _il.Emit(OpCodes.Ldtoken, funcMethod); - _il.Emit(OpCodes.Call, Types.MethodBaseGetMethodFromHandle); - _il.Emit(OpCodes.Castclass, typeof(MethodInfo)); - _il.Emit(OpCodes.Newobj, _ctx.Runtime!.TSFunctionCtor); - SetStackUnknown(); - return; - } - - // Fallback: null - _il.Emit(OpCodes.Ldnull); - SetStackType(StackType.Null); - } - private void StoreVariable(string name) { // A capture promoted into the enclosing function's display class (#625) is stored through @@ -325,7 +242,7 @@ private void StoreVariable(string name) /// /// Loads a variable value for populating a capture in a non-async arrow's display class. - /// This is similar to LoadVariable but designed for capture population. + /// This is similar to the EmitVariable override but designed for capture population. /// private void LoadVariableForCapture(string name) {