From d49cd3c552afa222ed1cbbee85a8d50d725902e7 Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Fri, 30 May 2025 11:38:22 -0700 Subject: [PATCH] [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 Reviewed-by: Mayank Patke --- pkg/compiler/lib/src/ssa/codegen.dart | 2 +- pkg/compiler/lib/src/ssa/codegen_helpers.dart | 2 +- .../lib/src/ssa/interceptor_finalizer.dart | 11 +- .../lib/src/ssa/interceptor_simplifier.dart | 13 +- .../src/ssa/invoke_dynamic_specializers.dart | 18 +-- pkg/compiler/lib/src/ssa/nodes.dart | 111 ++++++++---------- pkg/compiler/lib/src/ssa/optimize.dart | 37 +++--- .../lib/src/ssa/types_propagation.dart | 2 +- tests/web/regress/issue/60793_test.dart | 14 +++ 9 files changed, 103 insertions(+), 107 deletions(-) create mode 100644 tests/web/regress/issue/60793_test.dart diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart index 9be7a50407c..8a7efbe7f9f 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart @@ -2480,7 +2480,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { 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 b0a2214484f..cc0d8807109 100644 --- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart +++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart @@ -111,7 +111,7 @@ class SsaInstructionSelection extends HBaseVisitor // 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 fa4df4ca18a..c429a5e3373 100644 --- a/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart +++ b/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart @@ -62,8 +62,8 @@ class SsaFinalizeInterceptors extends HBaseVisitor } /// 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 @@ class SsaFinalizeInterceptors extends HBaseVisitor 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 @@ class SsaFinalizeInterceptors extends HBaseVisitor } 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 e3a6282ae30..96a9bac762a 100644 --- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart +++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart @@ -266,7 +266,7 @@ class SsaSimplifyInterceptors extends HBaseVisitor // 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 @@ class SsaSimplifyInterceptors extends HBaseVisitor 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 @@ class SsaSimplifyInterceptors extends HBaseVisitor ), ); } else if (user is HInvokeSuper && - user.isCallOnInterceptor(_closedWorld) && + user.isCallOnInterceptor && node == user.receiver && useCount(user, node) == 1) { interceptedClasses.addAll( @@ -402,7 +402,7 @@ class SsaSimplifyInterceptors extends HBaseVisitor // } void finishInvoke(HInvoke invoke, Selector selector) { - HInstruction callReceiver = invoke.getDartReceiver(_closedWorld)!; + HInstruction callReceiver = invoke.getDartReceiver()!; if (receiver.nonCheck() == callReceiver.nonCheck()) { Set interceptedClasses = _interceptorData .getInterceptedClassesOn(selector.name, _closedWorld); @@ -412,19 +412,20 @@ class SsaSimplifyInterceptors extends HBaseVisitor 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 300749e3876..5cd54c12c85 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 @@ class IndexSpecializer extends InvokeDynamicSpecializer { 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 @@ class CodeUnitAtSpecializer extends InvokeDynamicSpecializer { 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 @@ class RemoveLastSpecializer extends InvokeDynamicSpecializer { 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 @@ class ModuloSpecializer extends BinaryArithmeticSpecializer { // 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 @@ class CompareToSpecializer extends InvokeDynamicSpecializer { 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 @@ abstract class IdempotentStringOperationSpecializer JClosedWorld closedWorld, OptimizationTestLog? log, ) { - HInstruction receiver = instruction.getDartReceiver(closedWorld); + HInstruction receiver = instruction.getDartReceiver(); if (receiver .isStringOrNull(closedWorld.abstractValueDomain) .isDefinitelyTrue) { @@ -1937,7 +1937,7 @@ class PatternMatchSpecializer extends InvokeDynamicSpecializer { 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 @@ class RoundSpecializer extends InvokeDynamicSpecializer { JClosedWorld closedWorld, OptimizationTestLog? log, ) { - HInstruction receiver = instruction.getDartReceiver(closedWorld); + HInstruction receiver = instruction.getDartReceiver(); if (receiver .isNumberOrNull(closedWorld.abstractValueDomain) .isDefinitelyTrue) { @@ -1997,7 +1997,7 @@ class ToIntSpecializer extends InvokeDynamicSpecializer { 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 73faa7654cc..636be70116c 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -1334,7 +1334,7 @@ abstract class HInstruction implements SpannableWithEntity { 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 @@ abstract class HInstruction implements SpannableWithEntity { bool isConstantFalse() => false; bool isConstantTrue() => false; - bool isInterceptor(JClosedWorld closedWorld) => false; - bool isValid() { HValidator validator = HValidator(); validator.currentBlock = block; @@ -1914,6 +1912,12 @@ abstract class HInvoke extends HInstruction /// 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 @@ abstract class HInvoke extends HInstruction 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 @@ abstract class HInvokeDynamic extends HInvoke implements InstructionContext { : const InvokeDynamicSpecializer(), super(inputs, resultType) { isInterceptedCall = isIntercepted; + _isCallOnInterceptor = isIntercepted; + updateIsCallOnInterceptor(); } Selector get selector => _selector; @@ -2047,18 +2077,13 @@ abstract class HInvokeDynamic extends HInvoke implements InstructionContext { 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 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 @@ class HInvokeStatic extends HInvoke { bool isIntercepted = false, }) : super(inputs, type) { isInterceptedCall = isIntercepted; + _isCallOnInterceptor = isIntercepted; + updateIsCallOnInterceptor(); } @override @@ -2282,13 +2309,8 @@ class HInvokeSuper extends HInvokeStatic { 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 @@ -2374,28 +2396,12 @@ class HFieldGet extends HFieldAccess { } } - @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 @@ class HFieldSet extends HFieldAccess { receiver.isNull(domain).isPotentiallyTrue; @override - HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; + HInstruction getDartReceiver() => receiver; @override bool onlyThrowsNSM() => true; @@ -2478,7 +2484,7 @@ class HGetLength extends HInstruction { receiver.isNull(domain).isPotentiallyTrue; @override - HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; + HInstruction getDartReceiver() => receiver; @override bool onlyThrowsNSM() => true; @@ -2553,7 +2559,7 @@ class HReadModifyWrite extends HInstruction implements HLateInstruction { receiver.isNull(domain).isPotentiallyTrue; @override - HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; + HInstruction getDartReceiver() => receiver; @override bool onlyThrowsNSM() => true; @@ -3253,10 +3259,6 @@ class HConstant extends HInstruction { @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; @@ -3337,27 +3339,12 @@ class HThis extends HParameterValue { // [element] can be null for some synthetic members, e.g. `$signature`. 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(HVisitor visitor) => visitor.visitThis(this); @override bool isCodeMotionInvariant() => true; - @override - bool isInterceptor(JClosedWorld closedWorld) { - return closedWorld.interceptorData.isInterceptedClass( - sourceElement!.enclosingClass, - ); - } - @override String toString() => 'this'; } @@ -3633,9 +3620,6 @@ class HInterceptor extends HInstruction { inputs.add(constant); } - @override - bool isInterceptor(JClosedWorld closedWorld) => true; - @override _GvnType get _gvnType => _GvnType.interceptor; @override @@ -3670,9 +3654,8 @@ class HOneShotInterceptor extends HInvokeDynamic { ) : 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 @@ class HIndex extends HInstruction { bool get isMovable => false; @override - HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; + HInstruction getDartReceiver() => receiver; @override bool onlyThrowsNSM() => true; @override @@ -3806,7 +3789,7 @@ class HIndexAssign extends HInstruction { bool get isMovable => false; @override - HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; + HInstruction getDartReceiver() => receiver; @override bool onlyThrowsNSM() => true; @override @@ -3832,7 +3815,7 @@ class HCharCodeAt extends HInstruction { 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 4936e8ae41e..7eebfbf8e5f 100644 --- a/pkg/compiler/lib/src/ssa/optimize.dart +++ b/pkg/compiler/lib/src/ssa/optimize.dart @@ -1042,7 +1042,7 @@ class SsaInstructionSimplifier extends HBaseVisitor 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 @@ class SsaInstructionSimplifier extends HBaseVisitor 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 @@ class SsaInstructionSimplifier extends HBaseVisitor 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 @@ class SsaInstructionSimplifier extends HBaseVisitor 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 @@ class SsaInstructionSimplifier extends HBaseVisitor @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 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { 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 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { 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 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { bool isTrivialDeadStore(HInstruction instruction) { return instruction is HFieldSet && - isTrivialDeadStoreReceiver(instruction.getDartReceiver(closedWorld)); + isTrivialDeadStoreReceiver(instruction.getDartReceiver()); } bool isDeadCode(HInstruction instruction) { @@ -3335,11 +3334,7 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { 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 @@ class SsaRedundantPhiEliminator implements OptimizationPhase { 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 @@ class SsaLoadElimination extends HBaseVisitor @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 @@ class SsaLoadElimination extends HBaseVisitor @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 f6692b93f7a..8921a96c30a 100644 --- a/pkg/compiler/lib/src/ssa/types_propagation.dart +++ b/pkg/compiler/lib/src/ssa/types_propagation.dart @@ -438,7 +438,7 @@ class SsaTypePropagator extends HBaseVisitor 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 00000000000..bdfdd19b83b --- /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(); +}