diff --git a/pkg/front_end/lib/src/base/incremental_compiler.dart b/pkg/front_end/lib/src/base/incremental_compiler.dart index 3d4d1716541..91742e4d9f7 100644 --- a/pkg/front_end/lib/src/base/incremental_compiler.dart +++ b/pkg/front_end/lib/src/base/incremental_compiler.dart @@ -56,8 +56,8 @@ import 'package:kernel/kernel.dart' VariableSet, VisitorDefault, VisitorVoidMixin, - Member, - TypeParameterType; + TypeParameterType, + Field; import 'package:kernel/kernel.dart' as kernel show Combinator; import 'package:kernel/reference_from_index.dart'; import 'package:kernel/target/changed_structure_notifier.dart' @@ -2551,6 +2551,7 @@ class ExpressionEvaluationHelperImpl implements ExpressionEvaluationHelper { required ObjectAccessTarget target, required DartType receiverType, required Name name, + required bool setter, }) { // On a missing target, rewrite to a dynamic target instead. if (target.kind == ObjectAccessTargetKind.missing) { @@ -2565,14 +2566,37 @@ class ExpressionEvaluationHelperImpl implements ExpressionEvaluationHelper { ClassHierarchySubtypes subtypeInformation = hierarchy .computeSubtypesInformation(); Set foundMatchInLibrary = {}; + Set visited = {}; + nextSubtype: for (Class cls in subtypeInformation.getSubtypesOf( receiverType.classNode, )) { - for (Member member in cls.members) { - if (member.name.text == name.text) { - foundMatchInLibrary.add(cls.enclosingLibrary); - break; + if (cls.isAbstract) continue; + Class? clsOrSuper = cls; + while (clsOrSuper != null) { + if (!visited.add(clsOrSuper)) break; + for (Procedure procedure in clsOrSuper.procedures) { + if (procedure.name.text == name.text) { + // Name match. + if (procedure.isAbstract) continue; + if (setter && !procedure.isSetter) { + continue; + } else if (!setter && procedure.isSetter) { + continue; + } + foundMatchInLibrary.add(clsOrSuper.enclosingLibrary); + continue nextSubtype; + } } + for (Field field in clsOrSuper.fields) { + if (field.name.text == name.text) { + // Name match. + if (setter && !field.hasSetter) continue; + foundMatchInLibrary.add(clsOrSuper.enclosingLibrary); + continue nextSubtype; + } + } + clsOrSuper = clsOrSuper.superclass; } } // If we only found one such library we overwrite the name so the VM diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/type_inference/inference_visitor.dart index 57f53d1e78f..15d163d7615 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart @@ -10399,6 +10399,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase target: binaryTarget, name: binaryName, receiverType: leftType, + setter: false, ); if (overWritten != null) { binaryTarget = overWritten.target; @@ -10593,6 +10594,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase target: unaryTarget, name: unaryName, receiverType: expressionType, + setter: false, ); if (overWritten != null) { unaryTarget = overWritten.target; @@ -10754,6 +10756,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase target: readTarget, name: indexGetName, receiverType: receiverType, + setter: false, ); if (overWritten != null) { readTarget = overWritten.target; @@ -10909,6 +10912,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase target: writeTarget, name: indexSetName, receiverType: receiverType, + setter: true, ); if (overWritten != null) { writeTarget = overWritten.target; @@ -11126,6 +11130,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase target: writeTarget, name: propertyName, receiverType: receiverType, + setter: true, ); if (overWritten != null) { writeTarget = overWritten.target; @@ -16843,6 +16848,7 @@ abstract class ExpressionEvaluationHelper { required ObjectAccessTarget target, required DartType receiverType, required Name name, + required bool setter, }); } diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart index c4428a09ff4..1ae78dd1f99 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart @@ -3685,6 +3685,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor { target: target, name: name, receiverType: receiverType, + setter: false, ); if (overWritten != null) { target = overWritten.target; @@ -5111,6 +5112,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor { target: readTarget, name: propertyName, receiverType: receiverType, + setter: false, ); if (overWritten != null) { readTarget = overWritten.target; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml new file mode 100644 index 00000000000..2339aedd4b1 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml @@ -0,0 +1,46 @@ +# 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. + +# Definition, offset, method etc extracted by starting the VM with +# `-DDFE_VERBOSE=true`, e.g. +# ``` +# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \ +# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart +# ``` +# and then issuing the expression compilation. +# +# Private member from superclass. + +sources: + main.dart: | + import 'dart:developer'; + import 'lib.dart'; + + class _A extends B { + void foo() { + debugger(); + print("hello"); + } + } + + void main() { + _A().foo(); + } + lib.dart: | + abstract class B { + Object? _foo; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "#_A" +method: "foo" +static: false +offset: 85 +scriptUri: main.dart +expression: | + this._foo diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml.expect new file mode 100644 index 00000000000..b4b64844660 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this{dynamic}.#lib1::_foo; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml new file mode 100644 index 00000000000..6fb685e00db --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml @@ -0,0 +1,50 @@ +# 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. + +# Getter/setter from superclass. + +sources: + main.dart: | + import 'dart:developer'; + import 'lib.dart'; + + class _A extends B { + void foo() { + debugger(); + print("hello"); + } + } + + void main() { + _A().foo(); + } + lib.dart: | + import 'lib2.dart'; + abstract class B extends C { + Object? get _a => null; + set _b(dynamic x) {} + } + lib2.dart: | + abstract class C { + set _a(dynamic x) {} + Object? get _b => null; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "#_A" +method: "foo" +static: false +offset: 85 +scriptUri: main.dart +expression: | + () { + this._a; + this._a = 42; + this._b; + this._b = 42; + }() diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml.expect new file mode 100644 index 00000000000..46b3df82d81 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml.expect @@ -0,0 +1,9 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return (() → Null { + this{dynamic}.#lib1::_a; + this.{dynamic}#lib2::_a = 42; + this{dynamic}.#lib2::_b; + this.{dynamic}#lib1::_b = 42; + })(){() → Null}; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml new file mode 100644 index 00000000000..d21987c3bce --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml @@ -0,0 +1,46 @@ +# 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. + +# Only 1 non-abstract method. + +sources: + main.dart: | + import 'dart:developer'; + import 'lib.dart'; + + abstract class _A extends B { + void foo() { + debugger(); + print("hello"); + } + } + + abstract class C extends _A { + String? get _foo; + } + + void main() { + D().foo(); + } + lib.dart: | + import 'main.dart'; + abstract class B { + } + + class D extends C { + String get _foo => "hello"; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "#_A" +method: "foo" +static: false +offset: 94 +scriptUri: main.dart +expression: | + _foo diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml.expect new file mode 100644 index 00000000000..b4b64844660 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this{dynamic}.#lib1::_foo; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml new file mode 100644 index 00000000000..bd5c0064296 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml @@ -0,0 +1,46 @@ +# 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. + +# 2 non-abstract methods, but only 1 non-abstract class. + +sources: + main.dart: | + import 'dart:developer'; + import 'lib.dart'; + + abstract class _A extends B { + void foo() { + debugger(); + print("hello"); + } + } + + abstract class C extends _A { + String? get _foo => "C"; + } + + void main() { + D().foo(); + } + lib.dart: | + import 'main.dart'; + abstract class B { + } + + class D extends C { + String get _foo => "D"; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "#_A" +method: "foo" +static: false +offset: 94 +scriptUri: main.dart +expression: | + _foo diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml.expect new file mode 100644 index 00000000000..b4b64844660 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this{dynamic}.#lib1::_foo; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml new file mode 100644 index 00000000000..9eefdfd9c9d --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml @@ -0,0 +1,46 @@ +# 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. + +# 2 non-abstract methods, 2 non-abstract class. + +sources: + main.dart: | + import 'dart:developer'; + import 'lib.dart'; + + abstract class _A extends B { + void foo() { + debugger(); + print("hello"); + } + } + + class C extends _A { + String? get _foo => "C"; + } + + void main() { + D().foo(); + } + lib.dart: | + import 'main.dart'; + abstract class B { + } + + class D extends C { + String get _foo => "D"; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "#_A" +method: "foo" +static: false +offset: 94 +scriptUri: main.dart +expression: | + _foo diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml.expect new file mode 100644 index 00000000000..90e04bc1151 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this{dynamic}._foo; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml new file mode 100644 index 00000000000..fcbeebc6eb5 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml @@ -0,0 +1,39 @@ +# 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. + +# Late final field has a setter. + +sources: + main.dart: | + import 'dart:developer'; + import 'lib.dart'; + + class _A extends B { + void foo() { + debugger(); + print("hello"); + } + } + + void main() { + _A().foo(); + } + lib.dart: | + import 'main.dart'; + class B { + late final int? _a; + } + +definitions: [] +definition_types: [] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "#_A" +method: "foo" +static: false +offset: 85 +scriptUri: main.dart +expression: | + _a = 42 diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml.expect new file mode 100644 index 00000000000..e69239564cb --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this.{dynamic}#lib1::_a = 42;