-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add language tests for flow analysis conservative de-promotion scope.
These tests verify the scope over which a variable can become de-promoted due conservative modeling of demotion. The conservative model of demotion is used for loops, closures, switch statements, and try blocks. The implementation of flow analysis already passes these tests. I will shortly be making a spec clarification that brings the spec in line with the implementation. Change-Id: I22a5584a808169fa228c2972c25844c432916c06 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152920 Commit-Queue: Paul Berry <[email protected]> Reviewed-by: Leaf Petersen <[email protected]>
- Loading branch information
1 parent
0a5e69a
commit fe9596b
Showing
7 changed files
with
495 additions
and
0 deletions.
There are no files selected for viewing
44 changes: 44 additions & 0 deletions
44
tests/language/nnbd/flow_analysis/write_promoted_value_in_closure_error_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// 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. | ||
|
||
// Verifies that variables assigned in closures and local functions are | ||
// de-promoted at the top of the closure, since the closure may be invoked | ||
// multiple times. | ||
|
||
void functionExpression(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
var f = () { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the closure, so flow analysis cannot check that the assigned value is | ||
// an int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
x = 0; | ||
}; | ||
} | ||
} | ||
|
||
void localFunction(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
f() { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the closure, so flow analysis cannot check that the assigned value is | ||
// an int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
x = 0; | ||
} | ||
} | ||
} | ||
|
||
main() { | ||
functionExpression(0); | ||
localFunction(0); | ||
} |
141 changes: 141 additions & 0 deletions
141
tests/language/nnbd/flow_analysis/write_promoted_value_in_loop_error_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// 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. | ||
|
||
// Verifies that variables assigned in loops are de-promoted at the top of the | ||
// loop body, since the loop body be executed multiple times. | ||
|
||
void forLoopAssignInCondition(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
for (; 0 == 0 ? (x = 0) == 0 : true;) { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the loop, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
} | ||
} | ||
} | ||
|
||
void forLoopAssignInUpdater(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
for (;; x = 0) { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the loop, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
} | ||
} | ||
} | ||
|
||
void forLoopAssignInBody(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
for (;;) { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the loop, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
x = 0; | ||
} | ||
} | ||
} | ||
|
||
void forEachAssignInBody(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
for (var y in [0]) { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the loop, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
x = 0; | ||
} | ||
} | ||
} | ||
|
||
void whileAssignInCondition(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
while (0 == 0 ? (x = 0) == 0 : true) { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the loop, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
} | ||
} | ||
} | ||
|
||
void whileAssignInBody(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
while (true) { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the loop, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
x = 0; | ||
} | ||
} | ||
} | ||
|
||
void doAssignInCondition(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
do { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the loop, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
} while ((x = 0) == 0); | ||
} | ||
} | ||
|
||
void doAssignInBody(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
do { | ||
// The assignment to x does de-promote because it happens after the top of | ||
// the loop, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
x = 0; | ||
} while (true); | ||
} | ||
} | ||
|
||
main() { | ||
forLoopAssignInCondition(0); | ||
forLoopAssignInUpdater(0); | ||
forLoopAssignInBody(0); | ||
forEachAssignInBody(0); | ||
whileAssignInCondition(0); | ||
whileAssignInBody(0); | ||
doAssignInCondition(0); | ||
doAssignInBody(0); | ||
} |
76 changes: 76 additions & 0 deletions
76
tests/language/nnbd/flow_analysis/write_promoted_value_in_loop_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// 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. | ||
|
||
// Verifies that variables assigned in initialization parts of loops are not | ||
// de-promoted, since loop initialization only executes once. | ||
|
||
void forLoopWithoutDecl(Object x) { | ||
if (x is int) { | ||
for (x = 0;;) { | ||
// The assignment to x does not de-promote x because it happens before the | ||
// top of the loop, and it assigns an int (which is compatible with the | ||
// promoted type). | ||
x.isEven; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void forLoopWithoutDeclAssignInRHS(Object x) { | ||
if (x is int) { | ||
int y; | ||
for (y = (x = 0);;) { | ||
// The assignment to x does not de-promote x because it happens before the | ||
// top of the loop, and it assigns an int (which is compatible with the | ||
// promoted type). | ||
x.isEven; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void forLoopWithDeclAssignInRHS(Object x) { | ||
if (x is int) { | ||
for (int y = (x = 0);;) { | ||
// The assignment to x does not de-promote x because it happens before the | ||
// top of the loop, and it assigns an int (which is compatible with the | ||
// promoted type). | ||
x.isEven; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void forEachWithoutDecl(Object x) { | ||
if (x is int) { | ||
int y; | ||
for (y in [x = 0]) { | ||
// The assignment to x does not de-promote x because it happens before the | ||
// top of the loop, and it assigns an int (which is compatible with the | ||
// promoted type). | ||
x.isEven; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void forEachWithDecl(Object x) { | ||
if (x is int) { | ||
for (int y in [x = 0]) { | ||
// The assignment to x does not de-promote x because it happens before the | ||
// top of the loop, and it assigns an int (which is compatible with the | ||
// promoted type). | ||
x.isEven; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
main() { | ||
forLoopWithoutDecl(0); | ||
forLoopWithoutDeclAssignInRHS(0); | ||
forLoopWithDeclAssignInRHS(0); | ||
forEachWithoutDecl(0); | ||
forEachWithDecl(0); | ||
} |
54 changes: 54 additions & 0 deletions
54
tests/language/nnbd/flow_analysis/write_promoted_value_in_switch_error_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// 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. | ||
|
||
// Verifies that variables assigned in switch statement bodies are de-promoted | ||
// at the top of labelled case blocks, since the assignment may occur before a | ||
// branch to the labelled case block. | ||
|
||
void switchWithLabelAssignInCase(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
switch (x) { | ||
case 1: | ||
continue L; | ||
L: | ||
case 0: | ||
// The assignment to x does de-promote because it happens after the | ||
// label, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
x = 0; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void switchWithLabelAssignInDefault(Object x) { | ||
if (x is int) { | ||
print(x.isEven); // Verify that promotion occurred | ||
switch (x) { | ||
case 1: | ||
continue L; | ||
L: | ||
default: | ||
// The assignment to x does de-promote because it happens after the | ||
// label, so flow analysis cannot check that the assigned value is an | ||
// int at the time de-promotion occurs. | ||
print(x.isEven); | ||
// ^^^^^^ | ||
// [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER | ||
// [cfe] The getter 'isEven' isn't defined for the class 'Object'. | ||
x = 0; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
main() { | ||
switchWithLabelAssignInCase(0); | ||
switchWithLabelAssignInDefault(0); | ||
} |
Oops, something went wrong.