[dart2wasm] Fix context collection emptiness false positive.
In some cases, closure contexts were being left out of the parent chain of their children because they were empty at the time the child closure was created. If a usage appeared later in the visit of the parent, the context would no longer be empty but the child would already be created without a parent. This was easiest to recreate in sync* function because unlike async, it doesn't introduce hoisted helper variables (these immediately mark the parent as non-empty). In the attached bug the repro only happens with named parameters because TFA transforms the named parameter into a Let that introduces a variable before the closure with the usage in the let body after the closure. The new test explicitly introduces the same pattern of a variable declared before the closure and used after it. The fix here is to not eagerly check for emptiness of the parents. Instead we post-process the Contexts and relink the parent tree skipping any empty nodes. Bug: https://github.com/dart-lang/sdk/issues/63264 Change-Id: I2f75506b9fa879544b1a606d8f157fbd44ba8ce2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/500680 Commit-Queue: Nate Biggs <natebiggs@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
6c377d447b
commit
0af31e37bb
@@ -1351,7 +1351,7 @@ class Context {
|
||||
/// The parent of this context, corresponding to the lexically enclosing
|
||||
/// owner. This is null if the context is a member context, or if all contexts
|
||||
/// in the parent chain are skipped.
|
||||
final Context? parent;
|
||||
Context? parent;
|
||||
|
||||
/// The variables captured by this context.
|
||||
final List<VariableDeclaration> variables = [];
|
||||
@@ -1502,7 +1502,10 @@ class Closures {
|
||||
|
||||
void _collectContexts() {
|
||||
if (captures.isNotEmpty || _isThisCaptured) {
|
||||
_member.accept(_ContextCollector(this, translator.options.enableAsserts));
|
||||
_ContextCollector(
|
||||
this,
|
||||
translator.options.enableAsserts,
|
||||
).collect(_member);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1774,6 +1777,16 @@ class _ContextCollector extends RecursiveVisitor {
|
||||
|
||||
_ContextCollector(this.closures, this.enableAsserts);
|
||||
|
||||
void collect(Member member) {
|
||||
member.accept(this);
|
||||
|
||||
for (final context in closures.contexts.values) {
|
||||
while (context.parent?.isEmpty ?? false) {
|
||||
context.parent = context.parent!.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitAssertStatement(AssertStatement node) {
|
||||
if (enableAsserts) {
|
||||
@@ -1793,12 +1806,8 @@ class _ContextCollector extends RecursiveVisitor {
|
||||
currentContext == null ||
|
||||
node.parent is Constructor && !isInInitializer;
|
||||
Context? oldContext = currentContext;
|
||||
Context? parent = currentContext;
|
||||
while (parent != null && parent.isEmpty) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
bool containsThis = closures._isThisCaptured && outerMost;
|
||||
currentContext = Context(node, parent, containsThis);
|
||||
currentContext = Context(node, oldContext, containsThis);
|
||||
closures.contexts[node] = currentContext!;
|
||||
node.visitChildren(this);
|
||||
currentContext = oldContext;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2026, 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() {
|
||||
print(another());
|
||||
}
|
||||
|
||||
Iterable<Object?> another() sync* {
|
||||
for (int i = 0; i < 1; i++) {
|
||||
// Add another scope
|
||||
yield Object();
|
||||
}
|
||||
// Declare i before the closure.
|
||||
int i = 23;
|
||||
yield test(() => [1]);
|
||||
// Use i after the closure.
|
||||
print(i);
|
||||
}
|
||||
|
||||
Object? test(Iterable<Object?> Function() f) {
|
||||
return f();
|
||||
}
|
||||
Reference in New Issue
Block a user