From 7cc953699be910b0a46d6434ccccf94482ae2c37 Mon Sep 17 00:00:00 2001 From: Jens Johansen Date: Thu, 28 Nov 2024 16:00:11 +0000 Subject: [PATCH] [CFE] Fix setter from extension used in for loop Change-Id: I6cfbd7b8c5d72ea508dcdb7f0b15ddaab1e0700d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/398062 Commit-Queue: Jens Johansen Reviewed-by: Johnni Winther --- .../lib/src/type_inference/for_in.dart | 52 +++++++++++++++++++ .../src/type_inference/inference_visitor.dart | 2 + .../extension_setter_in_for_in_loop.dart | 13 +++++ ...n_setter_in_for_in_loop.dart.strong.expect | 19 +++++++ ..._in_for_in_loop.dart.strong.modular.expect | 19 +++++++ ..._in_for_in_loop.dart.strong.outline.expect | 15 ++++++ ...for_in_loop.dart.strong.transformed.expect | 25 +++++++++ ...in_for_in_loop.dart.textual_outline.expect | 4 ++ ..._loop.dart.textual_outline_modelled.expect | 4 ++ 9 files changed, 153 insertions(+) create mode 100644 pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart create mode 100644 pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.expect create mode 100644 pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.modular.expect create mode 100644 pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.outline.expect create mode 100644 pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.transformed.expect create mode 100644 pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.textual_outline.expect create mode 100644 pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.textual_outline_modelled.expect diff --git a/pkg/front_end/lib/src/type_inference/for_in.dart b/pkg/front_end/lib/src/type_inference/for_in.dart index 1ab9696263d8..e8f35b847875 100644 --- a/pkg/front_end/lib/src/type_inference/for_in.dart +++ b/pkg/front_end/lib/src/type_inference/for_in.dart @@ -7,6 +7,7 @@ import 'package:kernel/ast.dart'; import '../base/instrumentation.dart' show InstrumentationValueForMember; import '../codes/cfe_codes.dart'; +import '../kernel/hierarchy/class_member.dart'; import '../kernel/internal_ast.dart'; import 'inference_results.dart'; import 'inference_visitor.dart'; @@ -259,6 +260,57 @@ class StaticForInVariable implements ForInVariable { } } +class ExtensionSetForInVariable implements ForInVariable { + final ExtensionSet extensionSet; + DartType? setterType; + + ExtensionSetForInVariable(this.extensionSet); + + @override + DartType computeElementType(InferenceVisitorBase visitor) { + ExpressionInferenceResult receiverResult = visitor.inferExpression( + extensionSet.receiver, const UnknownType(), + isVoidAllowed: false); + + List extensionTypeArguments = + visitor.computeExtensionTypeArgument(extensionSet.extension, + extensionSet.explicitTypeArguments, receiverResult.inferredType, + treeNodeForTesting: extensionSet); + + DartType receiverType = visitor.getExtensionReceiverType( + extensionSet.extension, extensionTypeArguments); + + ObjectAccessTarget target = new ExtensionAccessTarget( + receiverType, + extensionSet.target, + null, + ClassMemberKind.Setter, + extensionTypeArguments); + + setterType = target.getSetterType(visitor); + return setterType!; + } + + @override + Expression inferAssignment(InferenceVisitorBase visitor, DartType rhsType) { + assert(setterType != null); + Expression rhs = visitor.ensureAssignable( + setterType!, rhsType, extensionSet.value, + errorTemplate: templateForInLoopElementTypeNotAssignable, + nullabilityErrorTemplate: + templateForInLoopElementTypeNotAssignableNullability, + nullabilityPartErrorTemplate: + templateForInLoopElementTypeNotAssignablePartNullability, + isVoidAllowed: true); + + extensionSet.value = rhs..parent = extensionSet; + ExpressionInferenceResult result = visitor.inferExpression( + extensionSet, const UnknownType(), + isVoidAllowed: true); + return result.expression; + } +} + class InvalidForInVariable implements ForInVariable { final Expression? expression; 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 a8ed367d7e3b..2ec89d1f6ad6 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart @@ -1690,6 +1690,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase return new SuperPropertyForInVariable(syntheticAssignment); } else if (syntheticAssignment is StaticSet) { return new StaticForInVariable(syntheticAssignment); + } else if (syntheticAssignment is ExtensionSet) { + return new ExtensionSetForInVariable(syntheticAssignment); } else if (syntheticAssignment is InvalidExpression || hasProblem) { return new InvalidForInVariable(syntheticAssignment); } else { diff --git a/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart new file mode 100644 index 000000000000..798acf9829c4 --- /dev/null +++ b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2024, 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. + +extension on String { + set foo(int value) {} + bar(List input) { + foo = 42; + for (foo in input) { + print("inside loop"); + } + } +} diff --git a/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.expect b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.expect new file mode 100644 index 000000000000..e284fc7e8b21 --- /dev/null +++ b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.expect @@ -0,0 +1,19 @@ +library; +import self as self; +import "dart:core" as core; + +extension /* unnamed */ _extension#0 on core::String { + method bar = self::_extension#0|bar; + method tearoff bar = self::_extension#0|get#bar; + set foo = self::_extension#0|set#foo; +} +static extension-member method _extension#0|set#foo(lowered final core::String #this, core::int value) → void {} +static extension-member method _extension#0|bar(lowered final core::String #this, core::List input) → dynamic { + self::_extension#0|set#foo(#this, 42); + for (final core::int #t1 in input) { + self::_extension#0|set#foo(#this, #t1); + core::print("inside loop"); + } +} +static extension-member method _extension#0|get#bar(lowered final core::String #this) → (core::List) → dynamic + return (core::List input) → dynamic => self::_extension#0|bar(#this, input); diff --git a/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.modular.expect b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.modular.expect new file mode 100644 index 000000000000..e284fc7e8b21 --- /dev/null +++ b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.modular.expect @@ -0,0 +1,19 @@ +library; +import self as self; +import "dart:core" as core; + +extension /* unnamed */ _extension#0 on core::String { + method bar = self::_extension#0|bar; + method tearoff bar = self::_extension#0|get#bar; + set foo = self::_extension#0|set#foo; +} +static extension-member method _extension#0|set#foo(lowered final core::String #this, core::int value) → void {} +static extension-member method _extension#0|bar(lowered final core::String #this, core::List input) → dynamic { + self::_extension#0|set#foo(#this, 42); + for (final core::int #t1 in input) { + self::_extension#0|set#foo(#this, #t1); + core::print("inside loop"); + } +} +static extension-member method _extension#0|get#bar(lowered final core::String #this) → (core::List) → dynamic + return (core::List input) → dynamic => self::_extension#0|bar(#this, input); diff --git a/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.outline.expect b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.outline.expect new file mode 100644 index 000000000000..fae253aa0641 --- /dev/null +++ b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.outline.expect @@ -0,0 +1,15 @@ +library; +import self as self; +import "dart:core" as core; + +extension /* unnamed */ _extension#0 on core::String { + method bar = self::_extension#0|bar; + method tearoff bar = self::_extension#0|get#bar; + set foo = self::_extension#0|set#foo; +} +static extension-member method _extension#0|set#foo(lowered final core::String #this, core::int value) → void + ; +static extension-member method _extension#0|bar(lowered final core::String #this, core::List input) → dynamic + ; +static extension-member method _extension#0|get#bar(lowered final core::String #this) → (core::List) → dynamic + return (core::List input) → dynamic => self::_extension#0|bar(#this, input); diff --git a/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.transformed.expect b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.transformed.expect new file mode 100644 index 000000000000..577b391feb07 --- /dev/null +++ b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.strong.transformed.expect @@ -0,0 +1,25 @@ +library; +import self as self; +import "dart:core" as core; + +extension /* unnamed */ _extension#0 on core::String { + method bar = self::_extension#0|bar; + method tearoff bar = self::_extension#0|get#bar; + set foo = self::_extension#0|set#foo; +} +static extension-member method _extension#0|set#foo(lowered final core::String #this, core::int value) → void {} +static extension-member method _extension#0|bar(lowered final core::String #this, core::List input) → dynamic { + self::_extension#0|set#foo(#this, 42); + { + synthesized core::Iterator :sync-for-iterator = input.{core::Iterable::iterator}{core::Iterator}; + for (; :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) { + final core::int #t1 = :sync-for-iterator.{core::Iterator::current}{core::int}; + { + self::_extension#0|set#foo(#this, #t1); + core::print("inside loop"); + } + } + } +} +static extension-member method _extension#0|get#bar(lowered final core::String #this) → (core::List) → dynamic + return (core::List input) → dynamic => self::_extension#0|bar(#this, input); diff --git a/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.textual_outline.expect b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.textual_outline.expect new file mode 100644 index 000000000000..70e48c1820f6 --- /dev/null +++ b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.textual_outline.expect @@ -0,0 +1,4 @@ +extension on String { + set foo(int value) {} + bar(List input) {} +} diff --git a/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.textual_outline_modelled.expect new file mode 100644 index 000000000000..11e4f6362249 --- /dev/null +++ b/pkg/front_end/testcases/regress/extension_setter_in_for_in_loop.dart.textual_outline_modelled.expect @@ -0,0 +1,4 @@ +extension on String { + bar(List input) {} + set foo(int value) {} +}