From 11ea496dd81cad9f55c1b5b6def5833e4ed6f0b1 Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Thu, 23 Jan 2025 11:41:00 -0800 Subject: [PATCH] [vm,dart2wasm,tfa] Handle 'Never' static type as unreachable in TFA When building a data flow summary, break control and data flow after invocations with static result type 'Never', expressing that code after such invocation is unreachable. TEST=pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart Issue: https://github.com/dart-lang/sdk/issues/59941 Change-Id: I2e5acd861e4192bae231d356aeacf921a14766f5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405443 Reviewed-by: Slava Egorov Commit-Queue: Alexander Markov --- .../type_flow/summary_collector.dart | 28 +++++++++++++------ .../summary_collector/control_flow.dart | 11 ++++++++ .../control_flow.dart.expect | 9 ++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart index 0411c7fb023c..e9bccf158591 100644 --- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart +++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart @@ -1275,29 +1275,39 @@ class SummaryCollector extends RecursiveResultVisitor { _declareVariable(decl, _typesBuilder.fromStaticType(decl.type, true)); } - Call _makeCall(TreeNode node, Selector selector, Args args, + TypeExpr _makeCall(TreeNode node, Selector selector, Args args, {bool isInstanceCreation = false}) { - Type? staticResultType = null; Member? target; if (selector is DirectSelector) { target = selector.member; } else if (selector is InterfaceSelector) { target = selector.member; } - if (target is Procedure && node is Expression) { - final returnType = target.function.returnType; + Type? staticResultType = null; + if (node is Expression) { final staticDartType = _staticDartType(node); - // TODO(dartbug.com/54200): static type cannot be trusted when - // function type is returned. - if (returnType is TypeParameterType || - (returnType != staticDartType && returnType is! FunctionType)) { - staticResultType = _typesBuilder.fromStaticType(staticDartType, true); + if (staticDartType is NeverType && + staticDartType.declaredNullability == Nullability.nonNullable) { + staticResultType = emptyType; + } else if (target is Procedure) { + final returnType = target.function.returnType; + // TODO(dartbug.com/54200): static type cannot be trusted when + // function type is returned. + if (returnType is TypeParameterType || + (returnType != staticDartType && returnType is! FunctionType)) { + staticResultType = _typesBuilder.fromStaticType(staticDartType, true); + } } } Call call = new Call(selector, args, staticResultType, isInstanceCreation); call.condition = _currentCondition; _summary.add(call); callSites[node] = call; + if (staticResultType is EmptyType) { + _currentCondition = emptyType; + _variableValues = _makeEmptyVariableValues(); + return emptyType; + } return call; } diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart b/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart index 89ffa3cc298e..d3886817f536 100644 --- a/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart +++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart @@ -324,4 +324,15 @@ void cast1(x) { bar(x); } +void never1(bool cond) { + int i = 1; + if (cond) { + i = 2; + returnNever(); + } + foo(i); +} + +Never returnNever() => throw 'bye'; + main() {} diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect index 4a8ae46f6c8a..2f2da641d1b6 100644 --- a/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect +++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect @@ -337,6 +337,15 @@ t1 = _TypeCheck (%x against #lib::C1) (for x as #lib::C1) t2 = _Call direct [#lib::foo] (t1) t3 = _Call direct [#lib::bar] (t1) RESULT: _T {}? +------------ never1 ------------ +%cond = _Parameter #0 [_T (dart.core::bool)+] +t1 = _Call direct [#lib::returnNever] () {%cond} +t2 = Not (%cond) +t3 = _Call direct [#lib::foo] (_T (dart.core::_Smi, 1)) {t2} +RESULT: _T {}? +------------ returnNever ------------ + +RESULT: _T {} ------------ main ------------ RESULT: _T {}?