Fix reachability logic for expression types.

Previously, we considered an expression unreachable if its type was
exactly `Never`.  This CL switches to using TypeSystem.isBottom, which
correctly handles types like `T extends Never`.

Change-Id: Ia6ce580caab6bc7ce7cceb1d0097b50f1da88f8a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/154746
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry
2020-07-21 18:56:25 +00:00
committed by commit-bot@chromium.org
parent e653358a6c
commit eae54cf02c
8 changed files with 235 additions and 2 deletions
@@ -217,7 +217,7 @@ class InvocationInferenceHelper {
expression.staticType = DynamicTypeImpl.instance;
} else {
expression.staticType = type;
if (identical(type, NeverTypeImpl.instance)) {
if (_typeSystem.isBottom(type)) {
_flowAnalysis?.flow?.handleExit();
}
}
@@ -112,7 +112,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
expression.staticType = _dynamicType;
} else {
expression.staticType = type;
if (identical(type, NeverTypeImpl.instance)) {
if (_typeSystem.isBottom(type)) {
_flowAnalysis?.flow?.handleExit();
}
}
@@ -0,0 +1,30 @@
// Copyright (c) 2020, 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.
// This test verifies that if a read is performed on a getter whose type is
// `Never?`, the resulting code block is considered reachable by flow analysis.
// SharedOptions=--enable-experiment=non-nullable
Never? get neverQuestionGetter => null;
void explicitNeverQuestionType(Object x, bool b) {
if (x is! int) {
if (b) {
neverQuestionGetter;
} else {
return;
}
}
// Since the read of `neverQuestionGetter` was reachable, `x` is not promoted
// to `int`.
x.isEven;
// ^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] unspecified
}
main() {
explicitNeverQuestionType(0, true);
}
@@ -0,0 +1,46 @@
// Copyright (c) 2020, 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.
// This test verifies that if a read is performed on a getter whose type is
// `Never`, the resulting code block is considered unreachable by flow analysis.
// SharedOptions=--enable-experiment=non-nullable
import 'package:expect/expect.dart';
Never get neverGetter => throw 'x';
void explicitNeverType(Object x, bool b) {
if (x is! int) {
if (b) {
neverGetter; // Unreachable
} else {
return;
}
}
// Since the read of `neverGetter` was unreachable, `x` is now promoted to
// `int`.
Expect.isTrue(x.isEven);
}
class TypeVarExtendsNever<T extends Never> {
T get tGetter => throw 'x';
void test(Object x, bool b) {
if (x is! int) {
if (b) {
tGetter; // Unreachable
} else {
return;
}
}
// Since the read of `tGetter` was unreachable, `x` is now promoted to
// `int`.
Expect.isTrue(x.isEven);
}
}
main() {
explicitNeverType(0, false);
TypeVarExtendsNever<Never>().test(0, false);
}
@@ -0,0 +1,30 @@
// Copyright (c) 2020, 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.
// This test verifies that if a method is invoked whose return type is `Never?`,
// the resulting code block is considered reachable by flow analysis.
// SharedOptions=--enable-experiment=non-nullable
Never? neverQuestionFunction() => null;
void explicitNeverQuestionType(Object x, bool b) {
if (x is! int) {
if (b) {
neverQuestionFunction();
} else {
return;
}
}
// Since completion of `neverQuestionFunction` was reachable, `x` is not
// promoted to `int`.
x.isEven;
// ^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] unspecified
}
main() {
explicitNeverQuestionType(0, true);
}
@@ -0,0 +1,46 @@
// Copyright (c) 2020, 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.
// This test verifies that if a method is invoked whose return type is `Never`,
// the resulting code block is considered unreachable by flow analysis.
// SharedOptions=--enable-experiment=non-nullable
import 'package:expect/expect.dart';
Never neverFunction() => throw 'x';
void explicitNeverType(Object x, bool b) {
if (x is! int) {
if (b) {
neverFunction(); // Unreachable
} else {
return;
}
}
// Since completion of `neverFunction` was unreachable, `x` is now promoted to
// `int`.
Expect.isTrue(x.isEven);
}
class TypeVarExtendsNever<T extends Never> {
T tMethod() => throw 'x';
void test(Object x, bool b) {
if (x is! int) {
if (b) {
tMethod(); // Unreachable
} else {
return;
}
}
// Since completion of `tMethod` was unreachable, `x` is now promoted to
// `int`.
Expect.isTrue(x.isEven);
}
}
main() {
explicitNeverType(0, false);
TypeVarExtendsNever<Never>().test(0, false);
}
@@ -0,0 +1,28 @@
// Copyright (c) 2020, 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.
// This test verifies that if a read is performed on a variable whose type is
// `Never?`, the resulting code block is considered reachable by flow analysis.
// SharedOptions=--enable-experiment=non-nullable
void explicitNeverQuestionType(Object x, bool b) {
Never? y = null;
if (x is! int) {
if (b) {
y;
} else {
return;
}
}
// Since the read of `y` was reachable, `x` is not promoted to `int`.
x.isEven;
// ^^^^^^
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER
// [cfe] unspecified
}
main() {
explicitNeverQuestionType(0, true);
}
@@ -0,0 +1,53 @@
// Copyright (c) 2020, 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.
// This test verifies that if a read is performed on a variable whose type is
// `Never`, the resulting code block is considered unreachable by flow analysis.
// SharedOptions=--enable-experiment=non-nullable
import 'package:expect/expect.dart';
void explicitNeverType(Never Function() f, Object x, bool b1, bool b2) {
late Never y;
// Loop so that flow analysis no longer can tell that y is definitely
// unassigned
while (true) {
if (x is! int) {
if (b1) {
y; // Unreachable
} else {
return;
}
}
// Since the read of `y` was unreachable, `x` is now promoted to `int`.
Expect.isTrue(x.isEven);
if (b2) return;
y = f();
}
}
void typeVarExtendsNever<T extends Never>(
T Function() f, Object x, bool b1, bool b2) {
late T y;
// Loop so that flow analysis no longer can tell that y is definitely
// unassigned
while (true) {
if (x is! int) {
if (b1) {
y; // Unreachable
} else {
return;
}
}
// Since the read of `y` was unreachable, `x` is now promoted to `int`.
Expect.isTrue(x.isEven);
if (b2) return;
y = f();
}
}
main() {
explicitNeverType(() => throw 'x', 0, false, true);
typeVarExtendsNever<Never>(() => throw 'x', 0, false, true);
}