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:
committed by
commit-bot@chromium.org
parent
e653358a6c
commit
eae54cf02c
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user