Flow analysis: Pin down initialization behaviors before fixing #1785.

https://github.com/dart-lang/language/issues/1785 has a wide enough
impact that its fix will have to be bundled in with a language version
(i.e. future versions of Dart will have to reproduce the old buggy
behavior for code that's not opted in to the latest language version).
Therefore, we'll have to maintain tests of the behavior both before
and after the fix.  This CL is the first step in that process, adding
tests that validate the current (buggy) behavior.

Bug: https://github.com/dart-lang/language/issues/1785
Change-Id: I78f17999ac1cbc096a312ef977db24654e06a263
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210400
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2021-08-25 20:32:02 +00:00 committed by commit-bot@chromium.org
parent cf46f2848f
commit d2b313227d
3 changed files with 291 additions and 0 deletions

View file

@ -11,6 +11,16 @@ finalLocalBool(int? x) {
}
}
finalLocalBool_untyped(int? x) {
final b = x == null;
if (!b) {
// No promotion due to https://github.com/dart-lang/language/issues/1785
x;
} else {
x;
}
}
localBool(int? x) {
bool b = x == null;
if (!b) {
@ -20,6 +30,16 @@ localBool(int? x) {
}
}
localBool_untyped(int? x) {
var b = x == null;
if (!b) {
// No promotion due to https://github.com/dart-lang/language/issues/1785
x;
} else {
x;
}
}
localBool_assigned(int? x, bool b1) {
bool b2 = b1;
b2 = x == null;
@ -30,6 +50,16 @@ localBool_assigned(int? x, bool b1) {
}
}
localBool_assigned_untyped(int? x, bool b1) {
var b2 = b1;
b2 = x == null;
if (!b2) {
/*nonNullable*/ x;
} else {
x;
}
}
localBool_assignedDynamic(int? x, bool b1) {
dynamic b2 = b1;
b2 = x == null;
@ -49,6 +79,15 @@ parameter_assigned(int? x, bool b) {
}
}
parameter_assigned_untyped(int? x, b) {
b = x == null;
if (!b) {
/*nonNullable*/ x;
} else {
x;
}
}
parameter_assignedDynamic(int? x, dynamic b) {
b = x == null;
if (!b) {
@ -69,6 +108,17 @@ lateFinalLocalBool(int? x) {
}
}
lateFinalLocalBool_untyped(int? x) {
late final b = x == null;
if (!b) {
// We don't promote based on the initializers of late locals because we
// don't know when they execute.
x;
} else {
x;
}
}
lateLocalBool(int? x) {
late bool b = x == null;
if (!b) {
@ -80,6 +130,17 @@ lateLocalBool(int? x) {
}
}
lateLocalBool_untyped(int? x) {
late var b = x == null;
if (!b) {
// We don't promote based on the initializers of late locals because we
// don't know when they execute.
x;
} else {
x;
}
}
lateLocalBool_assignedAndInitialized(int? x, bool b1) {
late bool b2 = b1;
b2 = x == null;
@ -90,6 +151,16 @@ lateLocalBool_assignedAndInitialized(int? x, bool b1) {
}
}
lateLocalBool_assignedAndInitialized_untyped(int? x, bool b1) {
late var b2 = b1;
b2 = x == null;
if (!b2) {
/*nonNullable*/ x;
} else {
x;
}
}
lateLocalBool_assignedButNotInitialized(int? x) {
late bool b;
b = x == null;
@ -100,6 +171,16 @@ lateLocalBool_assignedButNotInitialized(int? x) {
}
}
lateLocalBool_assignedButNotInitialized_untyped(int? x) {
late var b;
b = x == null;
if (!b) {
/*nonNullable*/ x;
} else {
x;
}
}
rebaseWithDemotion(int? x, int? y, int? z, int? a) {
x;
y;

View file

@ -13,6 +13,11 @@ localVariable() {
x;
}
localVariable_null() {
var x = null;
x;
}
localVariable_hasInitializer(num a) {
var x = a;
x = 1;

View file

@ -0,0 +1,205 @@
// Copyright (c) 2021, 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.
// @dart=2.13
import '../../static_type_helper.dart';
// This test checks whether a local boolean variable can be used to perform type
// promotion, if that variable is implicitly typed.
//
// Due to https://github.com/dart-lang/language/issues/1785, initializer
// expressions on implicitly typed variables are ignored for the purposes of
// type promotion (however, later assignments to those variables still do
// influence promotion). To avoid introducing breaking language changes, we
// intend to preserve this behavior until a specific Dart language version.
// This test verifies that for code that is not opted in to the newer behavior,
// the old (buggy) behavior persists.
parameterUnmodified(int? x) {
{
late final b = x != null;
// We wouldn't promote based on the initializers of late locals anyhow,
// because we don't know when they execute.
if (b) x.expectStaticType<Exactly<int?>>();
}
{
late final b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
late var b = x != null;
// We wouldn't promote based on the initializers of late locals anyhow,
// because we don't know when they execute.
if (b) x.expectStaticType<Exactly<int?>>();
}
{
late var b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
final b = x != null;
if (b) x.expectStaticType<Exactly<int?>>();
}
{
final b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
var b = x != null;
if (b) x.expectStaticType<Exactly<int?>>();
}
{
var b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
}
parameterModifiedLater(int? x, int? y) {
x = y;
{
late final b = x != null;
// We wouldn't promote based on the initializers of late locals anyhow,
// because we don't know when they execute.
if (b) x.expectStaticType<Exactly<int?>>();
}
{
late final b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
late var b = x != null;
// We wouldn't promote based on the initializers of late locals anyhow,
// because we don't know when they execute.
if (b) x.expectStaticType<Exactly<int?>>();
}
{
late var b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
final b = x != null;
if (b) x.expectStaticType<Exactly<int?>>();
}
{
final b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
var b = x != null;
if (b) x.expectStaticType<Exactly<int?>>();
}
{
var b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
}
localVariableInitialized(int? y) {
int? x = y;
{
late final b = x != null;
// We wouldn't promote based on the initializers of late locals anyhow,
// because we don't know when they execute.
if (b) x.expectStaticType<Exactly<int?>>();
}
{
late final b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
late var b = x != null;
// We wouldn't promote based on the initializers of late locals anyhow,
// because we don't know when they execute.
if (b) x.expectStaticType<Exactly<int?>>();
}
{
late var b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
final b = x != null;
if (b) x.expectStaticType<Exactly<int?>>();
}
{
final b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
var b = x != null;
if (b) x.expectStaticType<Exactly<int?>>();
}
{
var b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
}
localVariableModifiedLater(int? y) {
int? x;
x = y;
{
late final b = x != null;
// We wouldn't promote based on the initializers of late locals anyhow,
// because we don't know when they execute.
if (b) x.expectStaticType<Exactly<int?>>();
}
{
late final b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
late var b = x != null;
// We wouldn't promote based on the initializers of late locals anyhow,
// because we don't know when they execute.
if (b) x.expectStaticType<Exactly<int?>>();
}
{
late var b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
final b = x != null;
if (b) x.expectStaticType<Exactly<int?>>();
}
{
final b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
{
var b = x != null;
if (b) x.expectStaticType<Exactly<int?>>();
}
{
var b;
b = x != null;
if (b) x.expectStaticType<Exactly<int>>();
}
}
main() {
parameterUnmodified(null);
parameterUnmodified(0);
parameterModifiedLater(null, null);
parameterModifiedLater(null, 0);
localVariableInitialized(null);
localVariableInitialized(0);
localVariableModifiedLater(null);
localVariableModifiedLater(0);
}