From 7ff5e3654d37834033fbd0936b1ef486d5e74926 Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Tue, 2 Jun 2026 05:42:19 -0700 Subject: [PATCH] [tfa] Fix handling of late local variables with static type Never Initializers of late local variables are wrapped into closures. The result type of a closure is a static type of the variable. So initializer of a late variable with static type Never have a call to a closure with static result type Never. TFA summary collector handles calls with static result type Never specially, treating all subsequent code as unreachable (by setting control-dependent condition to empty). As a result, all subsequent code after late variable with static type Never (and initializer) is tree-shaken. The fix is to avoid propagating control-dependent condition out of the late variable initializers. TEST=pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart Fixes https://github.com/dart-lang/sdk/issues/63478 Change-Id: If41221777c9d249dd21ce5827c586b0f531cbce8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508363 Reviewed-by: Slava Egorov Commit-Queue: Alexander Markov --- .../type_flow/summary_collector.dart | 6 ++++ .../type_flow/transformer/regress_63478.dart | 24 +++++++++++++++ .../transformer/regress_63478.dart.expect | 30 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart create mode 100644 pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart.expect diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart index f1714649e07..ad55e732e32 100644 --- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart +++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart @@ -2817,6 +2817,7 @@ class SummaryCollector extends RecursiveResultVisitor { final variable = node.variable; variable.annotations.forEach(_visitAnnotation); final initializer = variable.initializer; + final savedCondition = _currentCondition; final TypeExpr initialValue = initializer == null ? ((variable.type.nullability == Nullability.nonNullable || variable.isLate) @@ -2824,6 +2825,11 @@ class SummaryCollector extends RecursiveResultVisitor { : _nullType) : _visit(initializer); _declareVariable(variable, initialValue); + if (variable.isLate) { + // Restore condition as initializer of a late variable + // is not evaluated immediately. + _currentCondition = savedCondition; + } return null; } diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart b/pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart new file mode 100644 index 00000000000..96ff6dd079c --- /dev/null +++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart @@ -0,0 +1,24 @@ +// 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. + +// Regression test for https://github.com/dart-lang/sdk/issues/63478. +// Verifies that initializer of a late variable doesn't affect +// subsequent statements. + +void test1() { + late var x = throw "error"; + print("reachable"); +} + +Never sayNever() => throw 'Never'; + +void test2() { + late Never y = sayNever(); + print("reachable"); +} + +void main() { + test1(); + test2(); +} diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart.expect new file mode 100644 index 00000000000..980722e091e --- /dev/null +++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_63478.dart.expect @@ -0,0 +1,30 @@ +library #lib; +import self as self; +import "dart:core" as core; + + +[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] +static method test1() → void { + function #x#initializer() → Never + return throw "error"; + late Never x = [@vm.inferred-type.metadata=? (receiver not int)] #x#initializer(){() → Never}; + core::print("reachable"); +} + +[@vm.inferred-return-type.metadata=!] +static method sayNever() → Never + return throw "Never"; + +[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] +static method test2() → void { + function #y#initializer() → Never + return self::sayNever(); + late Never y = [@vm.inferred-type.metadata=? (receiver not int)] #y#initializer(){() → Never}; + core::print("reachable"); +} + +[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] +static method main() → void { + self::test1(); + self::test2(); +}