[dart2js] Track, rather than compute, `isCallOnInterceptor`

By tracking `isCallOnInterceptor`, we avoid the need for class context
information in the `sourceElement` to compute the value. `sourceElement`
is now purely advisory to the choice of local names in codegen, and
can't be the wrong kind of element as in issue #60793.

Bug: #60793
Change-Id: I8bb68b6bf864a3a6f9f2beb40f68a6254431d49a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/432003
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 9be7a50..8a7efbe 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -2480,7 +2480,7 @@
     if (superElement is FieldEntity) {
       // TODO(sra): We can lower these in the simplifier.
       js.Name fieldName = _namer.instanceFieldPropertyName(superElement);
-      use(node.getDartReceiver(_closedWorld));
+      use(node.getDartReceiver());
       js.PropertyAccess access =
           js.PropertyAccess(
                 pop(),
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index b0a2214..cc0d880 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -111,7 +111,7 @@
       // The instructionType of [nullCheck] is not nullable (since it is the
       // (not) null check!) This means that if we do need to check the type, we
       // should test against nullCheck.checkedInput, not the direct input.
-      if (current!.getDartReceiver(_closedWorld) == nullCheck) {
+      if (current!.getDartReceiver() == nullCheck) {
         if (current is HFieldGet) return current;
         if (current is HFieldSet) return current;
         if (current is HGetLength) return current;
diff --git a/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart b/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart
index fa4df4c..c429a5e 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart
@@ -62,8 +62,8 @@
   }
 
   /// Returns `true` if [element] is an instance method that uses the
-  /// interceptor calling convention but the instance and interceptor arguments
-  /// will always be the same value.
+  /// interceptor calling convention but the interceptor argument will always be
+  /// the instance.
   bool usesSelfInterceptor(MemberEntity element) {
     if (!_interceptorData.isInterceptedMethod(element)) return false;
     ClassEntity cls = element.enclosingClass!;
@@ -101,6 +101,12 @@
     thisParameter!.instructionType = receiverParameter!.instructionType;
     receiverParameter.block!.rewrite(receiverParameter, thisParameter);
     receiverParameter.sourceElement = const _RenameToUnderscore();
+
+    for (final instruction in thisParameter.usedBy) {
+      if (instruction is HInvoke) {
+        instruction.updateIsCallOnInterceptor();
+      }
+    }
   }
 
   @override
@@ -262,6 +268,7 @@
   }
 
   void _replaceReceiverArgumentWithDummy(HInvoke node, int receiverIndex) {
+    assert(!node.isCallOnInterceptor, 'node: $node');
     ConstantValue constant = DummyInterceptorConstantValue();
     HConstant dummy = _graph.addConstant(constant, _closedWorld);
     node.replaceInput(receiverIndex, dummy);
diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
index e3a6282..96a9bac 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
@@ -266,7 +266,7 @@
     // If there is a call that dominates all other uses, we can use just the
     // selector of that instruction.
     if (dominator is HInvokeDynamic &&
-        dominator.isCallOnInterceptor(_closedWorld) &&
+        dominator.isCallOnInterceptor &&
         node == dominator.receiver &&
         useCount(dominator, node) == 1) {
       interceptedClasses = _interceptorData.getInterceptedClassesOn(
@@ -303,7 +303,7 @@
       interceptedClasses = {};
       for (HInstruction user in node.usedBy) {
         if (user is HInvokeDynamic &&
-            user.isCallOnInterceptor(_closedWorld) &&
+            user.isCallOnInterceptor &&
             node == user.receiver &&
             useCount(user, node) == 1) {
           interceptedClasses.addAll(
@@ -313,7 +313,7 @@
             ),
           );
         } else if (user is HInvokeSuper &&
-            user.isCallOnInterceptor(_closedWorld) &&
+            user.isCallOnInterceptor &&
             node == user.receiver &&
             useCount(user, node) == 1) {
           interceptedClasses.addAll(
@@ -402,7 +402,7 @@
     //     }
 
     void finishInvoke(HInvoke invoke, Selector selector) {
-      HInstruction callReceiver = invoke.getDartReceiver(_closedWorld)!;
+      HInstruction callReceiver = invoke.getDartReceiver()!;
       if (receiver.nonCheck() == callReceiver.nonCheck()) {
         Set<ClassEntity> interceptedClasses = _interceptorData
             .getInterceptedClassesOn(selector.name, _closedWorld);
@@ -412,19 +412,20 @@
           interceptedClasses: interceptedClasses,
         )) {
           invoke.changeUse(node, callReceiver);
+          invoke.updateIsCallOnInterceptor();
         }
       }
     }
 
     for (HInstruction user in node.usedBy.toList()) {
       if (user is HInvokeDynamic) {
-        if (user.isCallOnInterceptor(_closedWorld) &&
+        if (user.isCallOnInterceptor &&
             node == user.inputs[0] &&
             useCount(user, node) == 1) {
           finishInvoke(user, user.selector);
         }
       } else if (user is HInvokeSuper) {
-        if (user.isCallOnInterceptor(_closedWorld) &&
+        if (user.isCallOnInterceptor &&
             node == user.inputs[0] &&
             useCount(user, node) == 1) {
           finishInvoke(user, user.selector);
diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
index 300749e..5cd54c1 100644
--- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
+++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
@@ -361,7 +361,7 @@
     JClosedWorld closedWorld,
     OptimizationTestLog? log,
   ) {
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
     var abstractValueDomain = closedWorld.abstractValueDomain;
     if (receiver.isIndexablePrimitive(abstractValueDomain).isPotentiallyFalse) {
       return null;
@@ -419,7 +419,7 @@
     OptimizationTestLog? log,
   ) {
     final abstractValueDomain = closedWorld.abstractValueDomain;
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
     if (receiver.isStringOrNull(abstractValueDomain).isPotentiallyFalse) {
       return null;
     }
@@ -460,7 +460,7 @@
     JClosedWorld closedWorld,
     OptimizationTestLog? log,
   ) {
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
     final abstractValueDomain = closedWorld.abstractValueDomain;
     if (receiver.isGrowableArray(abstractValueDomain).isPotentiallyFalse) {
       return null;
@@ -876,7 +876,7 @@
     // track -0.0 precisely, we have to syntactically filter inputs that cannot
     // generate -0.0.
 
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
     if (inputsArePositiveIntegers(instruction, closedWorld) &&
         !canBeNegativeZero(receiver)) {
       return HRemainder(
@@ -1840,7 +1840,7 @@
     JClosedWorld closedWorld,
     OptimizationTestLog? log,
   ) {
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
     // `compareTo` has no side-effect (other than throwing) and can be GVN'ed
     // for some known types.
     if (receiver
@@ -1890,7 +1890,7 @@
     JClosedWorld closedWorld,
     OptimizationTestLog? log,
   ) {
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
     if (receiver
         .isStringOrNull(closedWorld.abstractValueDomain)
         .isDefinitelyTrue) {
@@ -1937,7 +1937,7 @@
     JClosedWorld closedWorld,
     OptimizationTestLog? log,
   ) {
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
     HInstruction pattern = instruction.inputs[2];
     if (receiver
             .isStringOrNull(closedWorld.abstractValueDomain)
@@ -1969,7 +1969,7 @@
     JClosedWorld closedWorld,
     OptimizationTestLog? log,
   ) {
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
     if (receiver
         .isNumberOrNull(closedWorld.abstractValueDomain)
         .isDefinitelyTrue) {
@@ -1997,7 +1997,7 @@
     JClosedWorld closedWorld,
     OptimizationTestLog? log,
   ) {
-    HInstruction receiver = instruction.getDartReceiver(closedWorld);
+    HInstruction receiver = instruction.getDartReceiver();
 
     // We would like to reduce `x.toInt()` to `x`. The web platform considers
     // infinities to be `int` values, but it is too hard to tell if an input is
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 73faa76..636be70 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1334,7 +1334,7 @@
   AbstractBool isPrimitiveOrNull(AbstractValueDomain domain) =>
       domain.isPrimitiveOrNull(instructionType);
 
-  HInstruction? getDartReceiver(JClosedWorld closedWorld) => null;
+  HInstruction? getDartReceiver() => null;
   bool onlyThrowsNSM() => false;
 
   bool isInBasicBlock() => block != null;
@@ -1481,8 +1481,6 @@
   bool isConstantFalse() => false;
   bool isConstantTrue() => false;
 
-  bool isInterceptor(JClosedWorld closedWorld) => false;
-
   bool isValid() {
     HValidator validator = HValidator();
     validator.currentBlock = block;
@@ -1914,6 +1912,12 @@
   /// input is the Dart receiver.
   bool isInterceptedCall = false;
 
+  /// [_isCallOnInterceptor] is true if this invocation uses the interceptor
+  /// calling convention *and* the interceptor input is an interceptor, and not
+  /// the receiver. A call has `isInterceptedCall == true` and
+  /// `_isCallOnInterceptor == false` after the 'self interceptor' optimization.
+  bool _isCallOnInterceptor = false;
+
   HInvoke(super.inputs, super.type) : super() {
     sideEffects.setAllSideEffects();
     sideEffects.setDependsOnSomething();
@@ -1929,6 +1933,30 @@
   void setAllocation(bool value) {
     _isAllocation = value;
   }
+
+  bool get isCallOnInterceptor => _isCallOnInterceptor;
+
+  /// Update 'isCallOnInterceptor'. An intercepted call can go through
+  /// refinements that drop references to unneeded values or arguments:
+  ///
+  ///     interceptor.foo(receiver, ...); // isCallOnInterceptor = true
+  /// -->
+  ///     receiver.foo(receiver, ...);    // isCallOnInterceptor = false
+  /// -->
+  ///     receiver.foo(dummy, ...);       // isCallOnInterceptor = false
+  void updateIsCallOnInterceptor() {
+    if (isInterceptedCall && _isCallOnInterceptor) {
+      final interceptor = inputs[0].nonCheck();
+      final receiver = inputs[1].nonCheck();
+      if (interceptor == receiver) {
+        _isCallOnInterceptor = false;
+      } else if (receiver case HConstant(
+        constant: DummyInterceptorConstantValue(),
+      )) {
+        _isCallOnInterceptor = false;
+      }
+    }
+  }
 }
 
 abstract class HInvokeDynamic extends HInvoke implements InstructionContext {
@@ -1978,6 +2006,8 @@
           : const InvokeDynamicSpecializer(),
       super(inputs, resultType) {
     isInterceptedCall = isIntercepted;
+    _isCallOnInterceptor = isIntercepted;
+    updateIsCallOnInterceptor();
   }
 
   Selector get selector => _selector;
@@ -2047,18 +2077,13 @@
   HInstruction get receiver => inputs[0];
 
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) {
-    return isCallOnInterceptor(closedWorld) ? inputs[1] : inputs[0];
+  HInstruction getDartReceiver() {
+    return _isCallOnInterceptor ? inputs[1] : inputs[0];
   }
 
   /// The type arguments passed in this dynamic invocation.
   List<DartType> get typeArguments;
 
-  /// Returns whether this call is on an interceptor object.
-  bool isCallOnInterceptor(JClosedWorld closedWorld) {
-    return isInterceptedCall && receiver.isInterceptor(closedWorld);
-  }
-
   @override
   _GvnType get _gvnType => _GvnType.invokeDynamic;
 
@@ -2238,6 +2263,8 @@
     bool isIntercepted = false,
   }) : super(inputs, type) {
     isInterceptedCall = isIntercepted;
+    _isCallOnInterceptor = isIntercepted;
+    updateIsCallOnInterceptor();
   }
 
   @override
@@ -2282,13 +2309,8 @@
 
   HInstruction get receiver => inputs[0];
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) {
-    return isCallOnInterceptor(closedWorld) ? inputs[1] : inputs[0];
-  }
-
-  /// Returns whether this call is on an interceptor object.
-  bool isCallOnInterceptor(JClosedWorld closedWorld) {
-    return isInterceptedCall && receiver.isInterceptor(closedWorld);
+  HInstruction getDartReceiver() {
+    return isCallOnInterceptor ? inputs[1] : inputs[0];
   }
 
   @override
@@ -2375,27 +2397,11 @@
   }
 
   @override
-  bool isInterceptor(JClosedWorld closedWorld) {
-    final entity = sourceElement;
-    // In case of a closure inside an interceptor class, JavaScript `this`, the
-    // interceptor, is stored in the generated closure class, and accessed
-    // through a [HFieldGet].
-    // TODO(sra): It would be better to track this as an explicit property
-    // rather than recover it from `sourceElement`.
-    if (entity is ThisLocal) {
-      return closedWorld.interceptorData.isInterceptedClass(
-        entity.enclosingClass,
-      );
-    }
-    return false;
-  }
-
-  @override
   bool canThrow(AbstractValueDomain domain) =>
       receiver.isNull(domain).isPotentiallyTrue;
 
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver;
+  HInstruction getDartReceiver() => receiver;
   @override
   bool onlyThrowsNSM() => true;
 
@@ -2425,7 +2431,7 @@
       receiver.isNull(domain).isPotentiallyTrue;
 
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver;
+  HInstruction getDartReceiver() => receiver;
   @override
   bool onlyThrowsNSM() => true;
 
@@ -2478,7 +2484,7 @@
       receiver.isNull(domain).isPotentiallyTrue;
 
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver;
+  HInstruction getDartReceiver() => receiver;
   @override
   bool onlyThrowsNSM() => true;
 
@@ -2553,7 +2559,7 @@
       receiver.isNull(domain).isPotentiallyTrue;
 
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver;
+  HInstruction getDartReceiver() => receiver;
   @override
   bool onlyThrowsNSM() => true;
 
@@ -3253,10 +3259,6 @@
   @override
   bool isConstantTrue() => constant is TrueConstantValue;
 
-  @override
-  bool isInterceptor(JClosedWorld closedWorld) =>
-      constant is InterceptorConstantValue;
-
   // Maybe avoid this if the literal is big?
   @override
   bool isCodeMotionInvariant() => true;
@@ -3338,27 +3340,12 @@
   HThis(ThisLocal? element, AbstractValue type) : super(element, type);
 
   @override
-  ThisLocal? get sourceElement => super.sourceElement as ThisLocal?;
-
-  @override
-  set sourceElement(covariant ThisLocal? local) {
-    super.sourceElement = local;
-  }
-
-  @override
   R accept<R>(HVisitor<R> visitor) => visitor.visitThis(this);
 
   @override
   bool isCodeMotionInvariant() => true;
 
   @override
-  bool isInterceptor(JClosedWorld closedWorld) {
-    return closedWorld.interceptorData.isInterceptedClass(
-      sourceElement!.enclosingClass,
-    );
-  }
-
-  @override
   String toString() => 'this';
 }
 
@@ -3634,9 +3621,6 @@
   }
 
   @override
-  bool isInterceptor(JClosedWorld closedWorld) => true;
-
-  @override
   _GvnType get _gvnType => _GvnType.interceptor;
   @override
   bool typeEquals(other) => other is HInterceptor;
@@ -3670,9 +3654,8 @@
   ) : super(selector, receiverType, null, inputs, true, resultType) {
     assert(inputs[0].isConstantNull());
     assert(selector.callStructure.typeArgumentCount == typeArguments.length);
+    _isCallOnInterceptor = true;
   }
-  @override
-  bool isCallOnInterceptor(JClosedWorld closedWorld) => true;
 
   @override
   String toString() =>
@@ -3767,7 +3750,7 @@
   bool get isMovable => false;
 
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver;
+  HInstruction getDartReceiver() => receiver;
   @override
   bool onlyThrowsNSM() => true;
   @override
@@ -3806,7 +3789,7 @@
   bool get isMovable => false;
 
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver;
+  HInstruction getDartReceiver() => receiver;
   @override
   bool onlyThrowsNSM() => true;
   @override
@@ -3832,7 +3815,7 @@
   bool get isMovable => false;
 
   @override
-  HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver;
+  HInstruction getDartReceiver() => receiver;
   @override
   bool onlyThrowsNSM() => true;
   @override
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 4936e8a..7eebfbf 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1042,7 +1042,7 @@
       if (folded != node) return folded;
     }
 
-    HInstruction receiver = node.getDartReceiver(_closedWorld);
+    HInstruction receiver = node.getDartReceiver();
     AbstractValue receiverType = receiver.instructionType;
     final element = _closedWorld.locateSingleMember(
       node.selector,
@@ -1090,8 +1090,7 @@
 
     if (element is FieldEntity && element.name == node.selector.name) {
       FieldEntity field = element;
-      if (!_nativeData.isNativeMember(field) &&
-          !node.isCallOnInterceptor(_closedWorld)) {
+      if (!_nativeData.isNativeMember(field) && !node.isCallOnInterceptor) {
         // Insertion point for the closure call.
         HInstruction insertionPoint = node;
         HInstruction load;
@@ -1991,7 +1990,7 @@
       HInstruction folded = handleInterceptedCall(node);
       if (folded != node) return folded;
     }
-    HInstruction receiver = node.getDartReceiver(_closedWorld);
+    HInstruction receiver = node.getDartReceiver();
     AbstractValue receiverType = receiver.instructionType;
 
     Selector selector = node.selector;
@@ -2102,7 +2101,7 @@
       if (folded != node) return folded;
     }
 
-    HInstruction receiver = node.getDartReceiver(_closedWorld);
+    HInstruction receiver = node.getDartReceiver();
     AbstractValue receiverType = receiver.instructionType;
     final member = node.element ??= _closedWorld.locateSingleMember(
       node.selector,
@@ -2175,7 +2174,7 @@
 
   @override
   HInstruction visitInvokeClosure(HInvokeClosure node) {
-    HInstruction closure = node.getDartReceiver(_closedWorld);
+    HInstruction closure = node.getDartReceiver();
 
     // Replace indirect call to static method tear-off closure with direct call
     // to static method.
@@ -3169,10 +3168,10 @@
 
     if (!instruction.onlyThrowsNSM()) return false;
 
-    final receiver = instruction.getDartReceiver(closedWorld);
+    final receiver = instruction.getDartReceiver();
     HInstruction? current = instruction.next;
     do {
-      if ((current!.getDartReceiver(closedWorld) == receiver) &&
+      if ((current!.getDartReceiver() == receiver) &&
           current.canThrow(_abstractValueDomain)) {
         return true;
       }
@@ -3218,9 +3217,9 @@
       if (use is HFieldSet) {
         // The use must be the receiver.  Even if the use is also the argument,
         // i.e.  a.x = a, the store is still dead if all other uses are dead.
-        if (use.getDartReceiver(closedWorld) == instruction) return true;
+        if (use.getDartReceiver() == instruction) return true;
       } else if (use is HFieldGet) {
-        assert(use.getDartReceiver(closedWorld) == instruction);
+        assert(use.getDartReceiver() == instruction);
         if (isDeadCode(use)) return true;
       }
       return false;
@@ -3236,7 +3235,7 @@
 
   bool isTrivialDeadStore(HInstruction instruction) {
     return instruction is HFieldSet &&
-        isTrivialDeadStoreReceiver(instruction.getDartReceiver(closedWorld));
+        isTrivialDeadStoreReceiver(instruction.getDartReceiver());
   }
 
   bool isDeadCode(HInstruction instruction) {
@@ -3335,11 +3334,7 @@
         final phiBlock = phi.block!;
         phiBlock.rewrite(phi, replacement);
         phiBlock.removePhi(phi);
-        if (replacement.sourceElement == null &&
-            phi.sourceElement != null &&
-            replacement is! HThis) {
-          replacement.sourceElement = phi.sourceElement;
-        }
+        replacement.sourceElement ??= phi.sourceElement;
         return;
       }
     }
@@ -3714,11 +3709,7 @@
       final phiBlock = phi.block!;
       phiBlock.rewrite(phi, candidate);
       phiBlock.removePhi(phi);
-      if (candidate.sourceElement == null &&
-          phi.sourceElement != null &&
-          candidate is! HThis) {
-        candidate.sourceElement = phi.sourceElement;
-      }
+      candidate.sourceElement ??= phi.sourceElement;
     }
   }
 
@@ -4508,7 +4499,7 @@
   @override
   void visitFieldGet(HFieldGet node) {
     FieldEntity element = node.element;
-    HInstruction receiver = node.getDartReceiver(_closedWorld).nonCheck();
+    HInstruction receiver = node.getDartReceiver().nonCheck();
     _visitFieldGet(element, receiver, node);
   }
 
@@ -4545,7 +4536,7 @@
   @override
   void visitFieldSet(HFieldSet node) {
     FieldEntity element = node.element;
-    HInstruction receiver = node.getDartReceiver(_closedWorld).nonCheck();
+    HInstruction receiver = node.getDartReceiver().nonCheck();
     if (memorySet.registerFieldValueUpdate(element, receiver, node.value)) {
       node.block!.remove(node);
     }
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
index f6692b9..8921a96 100644
--- a/pkg/compiler/lib/src/ssa/types_propagation.dart
+++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -438,7 +438,7 @@
       pendingOptimizations.putIfAbsent(node, () => checkInputs);
     }
 
-    HInstruction receiver = node.getDartReceiver(closedWorld);
+    HInstruction receiver = node.getDartReceiver();
     AbstractValue receiverType = receiver.instructionType;
     node.updateReceiverType(abstractValueDomain, receiverType);
 
diff --git a/tests/web/regress/issue/60793_test.dart b/tests/web/regress/issue/60793_test.dart
new file mode 100644
index 0000000..bdfdd19
--- /dev/null
+++ b/tests/web/regress/issue/60793_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+void main() {
+  void foo() {
+    final f = switch (null) {
+      _ => foo,
+    };
+    print(f);
+  }
+
+  foo();
+}