[dart2js] Fix implicitly used free type variable not being registered in closure scope.
Fixes: https://github.com/dart-lang/sdk/issues/54209
Change-Id: Ia042ac4444ebe134107ab5d626a3328d49b38241
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/339381
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index 463dc88..0b2385b 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -431,6 +431,14 @@
}
enterNewScope(node, () {
visitNode(node.variable);
+ if (node.isAsync) {
+ // If this is async then the type is explicitly used to instantiate
+ // the underlying StreamIterator.
+ visitInContext(
+ node.variable.type,
+ VariableUse.constructorTypeArgument(
+ _coreTypes.streamIteratorDefaultConstructor));
+ }
visitInVariableScope(node, () {
visitNode(node.iterable);
visitNode(node.body);
diff --git a/pkg/compiler/lib/src/ssa/locals_handler.dart b/pkg/compiler/lib/src/ssa/locals_handler.dart
index 8d946d2..6ca042c 100644
--- a/pkg/compiler/lib/src/ssa/locals_handler.dart
+++ b/pkg/compiler/lib/src/ssa/locals_handler.dart
@@ -250,20 +250,18 @@
// ClosureRepresentationInfos).
final scopeInfo = _scopeInfo;
if (scopeInfo is ClosureRepresentationInfo && scopeInfo.isClosure) {
- ClosureRepresentationInfo closureData = scopeInfo;
// If the freeVariableMapping is not empty, then this function was a
// nested closure that captures variables. Redirect the captured
// variables to fields in the closure.
- closureData.forEachFreeVariable(_localsMap!,
- (Local from, FieldEntity to) {
+ scopeInfo.forEachFreeVariable(_localsMap!, (Local from, FieldEntity to) {
redirectElement(from, to);
});
// Inside closure redirect references to itself to [:this:].
- HThis thisInstruction = HThis(closureData.thisLocal as ThisLocal?,
- _abstractValueDomain.nonNullType);
+ HThis thisInstruction = HThis(
+ scopeInfo.thisLocal as ThisLocal?, _abstractValueDomain.nonNullType);
builder.graph.thisInstruction = thisInstruction;
builder.graph.entry.addAtEntry(thisInstruction);
- updateLocal(closureData.getClosureEntity(_localsMap!)!, thisInstruction);
+ updateLocal(scopeInfo.getClosureEntity(_localsMap!)!, thisInstruction);
} else if (element.isInstanceMember) {
// Once closures have been mapped to classes their instance members might
// not have any thisElement if the closure was created inside a static
diff --git a/tests/web/regress/issue/54209_test.dart b/tests/web/regress/issue/54209_test.dart
new file mode 100644
index 0000000..27dfc4e
--- /dev/null
+++ b/tests/web/regress/issue/54209_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2023, 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.
+
+// Implicit uses of `foo.T` were not being registered in the local closure's
+// scope. The `await for` was then trying to lookup the `Foo.T` to register it
+// as the type argument to Stream. But the lookup failed because it `Foo.T`
+// wasn't registered.
+
+class A {
+ Future<void> foo<T>(Stream<T> Function() f) async {
+ await (() async {
+ await for (var v in f()) {
+ print(v);
+ }
+ }());
+ }
+}
+
+void main() {
+ A().foo<int>(() => Stream<int>.value(3));
+}