0af31e37bb
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>
24 lines
553 B
Dart
24 lines
553 B
Dart
// 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();
|
|
}
|