Skip to content

Commit da9b6a7

Browse files
kallentuCommit Queue
authored and
Commit Queue
committed
[flow] Part 2 Issue 3658 - Forward IfNullSet let expressions in the CFE.
This is a small change that allows forwarding let expressions for IfNullSets in the CFE. Since we now store the promotion information for a written expression in an if-statement, the CFE builds a let expression and it's not recognized by flow analysis. To fix this, we add a forward so flow analysis recognizes that it's the same expression that it's trying to find. language/inference_update_4/assignment_promotion_in_if_statement_test passes from this change. Bug: dart-lang/language#3658 Change-Id: I4ec99de036498869b0d9c5c652ca748d9f5e570a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/391861 Reviewed-by: Johnni Winther <[email protected]> Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 490dc01 commit da9b6a7

10 files changed

+450
-0
lines changed

pkg/front_end/lib/src/type_inference/inference_visitor.dart

+6
Original file line numberDiff line numberDiff line change
@@ -6002,6 +6002,12 @@ class InferenceVisitorImpl extends InferenceVisitorBase
60026002
replacement = new Let(readVariable, conditional)
60036003
..fileOffset = node.fileOffset;
60046004
}
6005+
6006+
// Forward the expression in cases where flow analysis needs to use the
6007+
// expression information. For example, for keeping the promotion in the
6008+
// following if statement in `if ((x ??= 2) == null) { ... }`.
6009+
flowAnalysis.forwardExpression(replacement, writeResult.expression);
6010+
60056011
return createNullAwareExpressionInferenceResult(
60066012
inferredType, replacement, nullAwareGuards);
60076013
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests the promotion of assignment expressions in if statements as described
6+
// by https://github.com/dart-lang/language/issues/3658
7+
8+
int? nullableInt() => 1;
9+
10+
notEqualNull_assignIfNull() {
11+
int? x = null;
12+
if ((x ??= nullableInt()) != null) {
13+
x.isEven; // x promoted to int
14+
}
15+
}
16+
17+
notEqualNullNull_eq() {
18+
int? x = null;
19+
if ((x = nullableInt()) != null) {
20+
x.isEven; // x promoted to int
21+
}
22+
}
23+
24+
is_eq() {
25+
int? x = null;
26+
if ((x = nullableInt()) is int) {
27+
x.isEven; // x promoted to int
28+
}
29+
}
30+
31+
is_plusEq() {
32+
num x = 2;
33+
if ((x += 1) is int) {
34+
x.isEven; // x promoted to int
35+
}
36+
}
37+
38+
is_postfix() {
39+
num x = 2;
40+
if ((x++) is int) {
41+
// No promotion because the value being is checked is the value of `x`
42+
// before the increment, and that value isn't relevant after the increment
43+
// occurs.
44+
x.isEven; // Error.
45+
}
46+
}
47+
48+
is_prefix() {
49+
num x = 2;
50+
if ((++x) is int) {
51+
x.isEven; // x promoted to int
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/inference_update_4/assignment_promotion_in_if_statement.dart:44:7: Error: The getter 'isEven' isn't defined for the class 'num'.
6+
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEven'.
7+
// x.isEven; // Error.
8+
// ^^^^^^
9+
//
10+
import self as self;
11+
import "dart:core" as core;
12+
13+
static method nullableInt() → core::int?
14+
return 1;
15+
static method notEqualNull_assignIfNull() → dynamic {
16+
has-declared-initializer core::int? x = null;
17+
if(!((let final core::int? #t1 = x in #t1 == null ?{core::int?} x = self::nullableInt() : #t1{core::int}) == null)) {
18+
x{core::int}.{core::int::isEven}{core::bool};
19+
}
20+
}
21+
static method notEqualNullNull_eq() → dynamic {
22+
has-declared-initializer core::int? x = null;
23+
if(!((x = self::nullableInt()) == null)) {
24+
x{core::int}.{core::int::isEven}{core::bool};
25+
}
26+
}
27+
static method is_eq() → dynamic {
28+
has-declared-initializer core::int? x = null;
29+
if((x = self::nullableInt()) is core::int) {
30+
x{core::int}.{core::int::isEven}{core::bool};
31+
}
32+
}
33+
static method is_plusEq() → dynamic {
34+
core::num x = 2;
35+
if((x = x.{core::num::+}(1){(core::num) → core::num}) is core::int) {
36+
x{core::int}.{core::int::isEven}{core::bool};
37+
}
38+
}
39+
static method is_postfix() → dynamic {
40+
core::num x = 2;
41+
if((let final core::num #t2 = x in let final core::num #t3 = x = #t2.{core::num::+}(1){(core::num) → core::num} in #t2) is core::int) {
42+
invalid-expression "pkg/front_end/testcases/inference_update_4/assignment_promotion_in_if_statement.dart:44:7: Error: The getter 'isEven' isn't defined for the class 'num'.
43+
Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEven'.
44+
x.isEven; // Error.
45+
^^^^^^" in x{<unresolved>}.isEven;
46+
}
47+
}
48+
static method is_prefix() → dynamic {
49+
core::num x = 2;
50+
if((x = x.{core::num::+}(1){(core::num) → core::num}) is core::int) {
51+
x{core::int}.{core::int::isEven}{core::bool};
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/inference_update_4/assignment_promotion_in_if_statement.dart:44:7: Error: The getter 'isEven' isn't defined for the class 'num'.
6+
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEven'.
7+
// x.isEven; // Error.
8+
// ^^^^^^
9+
//
10+
import self as self;
11+
import "dart:core" as core;
12+
13+
static method nullableInt() → core::int?
14+
return 1;
15+
static method notEqualNull_assignIfNull() → dynamic {
16+
has-declared-initializer core::int? x = null;
17+
if(!((let final core::int? #t1 = x in #t1 == null ?{core::int?} x = self::nullableInt() : #t1{core::int}) == null)) {
18+
x{core::int}.{core::int::isEven}{core::bool};
19+
}
20+
}
21+
static method notEqualNullNull_eq() → dynamic {
22+
has-declared-initializer core::int? x = null;
23+
if(!((x = self::nullableInt()) == null)) {
24+
x{core::int}.{core::int::isEven}{core::bool};
25+
}
26+
}
27+
static method is_eq() → dynamic {
28+
has-declared-initializer core::int? x = null;
29+
if((x = self::nullableInt()) is core::int) {
30+
x{core::int}.{core::int::isEven}{core::bool};
31+
}
32+
}
33+
static method is_plusEq() → dynamic {
34+
core::num x = 2;
35+
if((x = x.{core::num::+}(1){(core::num) → core::num}) is core::int) {
36+
x{core::int}.{core::int::isEven}{core::bool};
37+
}
38+
}
39+
static method is_postfix() → dynamic {
40+
core::num x = 2;
41+
if((let final core::num #t2 = x in let final core::num #t3 = x = #t2.{core::num::+}(1){(core::num) → core::num} in #t2) is core::int) {
42+
invalid-expression "pkg/front_end/testcases/inference_update_4/assignment_promotion_in_if_statement.dart:44:7: Error: The getter 'isEven' isn't defined for the class 'num'.
43+
Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEven'.
44+
x.isEven; // Error.
45+
^^^^^^" in x{<unresolved>}.isEven;
46+
}
47+
}
48+
static method is_prefix() → dynamic {
49+
core::num x = 2;
50+
if((x = x.{core::num::+}(1){(core::num) → core::num}) is core::int) {
51+
x{core::int}.{core::int::isEven}{core::bool};
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method nullableInt() → core::int?
6+
;
7+
static method notEqualNull_assignIfNull() → dynamic
8+
;
9+
static method notEqualNullNull_eq() → dynamic
10+
;
11+
static method is_eq() → dynamic
12+
;
13+
static method is_plusEq() → dynamic
14+
;
15+
static method is_postfix() → dynamic
16+
;
17+
static method is_prefix() → dynamic
18+
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/inference_update_4/assignment_promotion_in_if_statement.dart:44:7: Error: The getter 'isEven' isn't defined for the class 'num'.
6+
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEven'.
7+
// x.isEven; // Error.
8+
// ^^^^^^
9+
//
10+
import self as self;
11+
import "dart:core" as core;
12+
13+
static method nullableInt() → core::int?
14+
return 1;
15+
static method notEqualNull_assignIfNull() → dynamic {
16+
has-declared-initializer core::int? x = null;
17+
if(!((let final core::int? #t1 = x in #t1 == null ?{core::int?} x = self::nullableInt() : #t1{core::int}) == null)) {
18+
x{core::int}.{core::int::isEven}{core::bool};
19+
}
20+
}
21+
static method notEqualNullNull_eq() → dynamic {
22+
has-declared-initializer core::int? x = null;
23+
if(!((x = self::nullableInt()) == null)) {
24+
x{core::int}.{core::int::isEven}{core::bool};
25+
}
26+
}
27+
static method is_eq() → dynamic {
28+
has-declared-initializer core::int? x = null;
29+
if((x = self::nullableInt()) is core::int) {
30+
x{core::int}.{core::int::isEven}{core::bool};
31+
}
32+
}
33+
static method is_plusEq() → dynamic {
34+
core::num x = 2;
35+
if((x = x.{core::num::+}(1){(core::num) → core::num}) is core::int) {
36+
x{core::int}.{core::int::isEven}{core::bool};
37+
}
38+
}
39+
static method is_postfix() → dynamic {
40+
core::num x = 2;
41+
if((let final core::num #t2 = x in let final core::num #t3 = x = #t2.{core::num::+}(1){(core::num) → core::num} in #t2) is core::int) {
42+
invalid-expression "pkg/front_end/testcases/inference_update_4/assignment_promotion_in_if_statement.dart:44:7: Error: The getter 'isEven' isn't defined for the class 'num'.
43+
Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEven'.
44+
x.isEven; // Error.
45+
^^^^^^" in x{<unresolved>}.isEven;
46+
}
47+
}
48+
static method is_prefix() → dynamic {
49+
core::num x = 2;
50+
if((x = x.{core::num::+}(1){(core::num) → core::num}) is core::int) {
51+
x{core::int}.{core::int::isEven}{core::bool};
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
int? nullableInt() => 1;
2+
3+
notEqualNull_assignIfNull() {}
4+
5+
notEqualNullNull_eq() {}
6+
7+
is_eq() {}
8+
9+
is_plusEq() {}
10+
11+
is_postfix() {}
12+
13+
is_prefix() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
int? nullableInt() => 1;
2+
3+
is_eq() {}
4+
5+
is_plusEq() {}
6+
7+
is_postfix() {}
8+
9+
is_prefix() {}
10+
11+
notEqualNullNull_eq() {}
12+
13+
notEqualNull_assignIfNull() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//
2+
// Problems in component:
3+
//
4+
// sdk/lib/core/core.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
5+
//
6+
// sdk/lib/async/async.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
7+
//
8+
// sdk/lib/collection/collection.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
9+
//
10+
// sdk/lib/_internal/vm_shared/lib/compact_hash.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
11+
//
12+
// sdk/lib/concurrent/concurrent.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
13+
//
14+
// sdk/lib/convert/convert.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
15+
//
16+
// sdk/lib/developer/developer.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
17+
//
18+
// sdk/lib/ffi/ffi.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
19+
//
20+
// sdk/lib/internal/internal.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
21+
//
22+
// sdk/lib/isolate/isolate.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
23+
//
24+
// sdk/lib/math/math.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
25+
//
26+
// sdk/lib/mirrors/mirrors.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
27+
//
28+
// sdk/lib/typed_data/typed_data.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
29+
//
30+
// sdk/lib/_internal/vm/bin/vmservice_io.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
31+
//
32+
// sdk/lib/vmservice/vmservice.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
33+
//
34+
// sdk/lib/_internal/vm/bin/builtin.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
35+
//
36+
// sdk/lib/html/dartium/nativewrappers.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
37+
//
38+
// sdk/lib/io/io.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
39+
//
40+
// sdk/lib/cli/cli.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
41+
//
42+
library;
43+
//
44+
// Problems in library:
45+
//
46+
// pkg/front_end/testcases/inference_update_4/assignment_promotion_in_if_statement.dart:44:7: Error: The getter 'isEven' isn't defined for the class 'num'.
47+
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEven'.
48+
// x.isEven; // Error.
49+
// ^^^^^^
50+
//
51+
import self as self;
52+
import "dart:core" as core;
53+
54+
static method nullableInt() → core::int?
55+
return 1;
56+
static method notEqualNull_assignIfNull() → dynamic {
57+
has-declared-initializer core::int? x = null;
58+
if(!((let final core::int? #t1 = x in #t1 == null ?{core::int?} x = self::nullableInt() : #t1{core::int}) == null)) {
59+
x{core::int}.{core::int::isEven}{core::bool};
60+
}
61+
}
62+
static method notEqualNullNull_eq() → dynamic {
63+
has-declared-initializer core::int? x = null;
64+
if(!((x = self::nullableInt()) == null)) {
65+
x{core::int}.{core::int::isEven}{core::bool};
66+
}
67+
}
68+
static method is_eq() → dynamic {
69+
has-declared-initializer core::int? x = null;
70+
if((x = self::nullableInt()) is core::int) {
71+
x{core::int}.{core::int::isEven}{core::bool};
72+
}
73+
}
74+
static method is_plusEq() → dynamic {
75+
core::num x = 2;
76+
if((x = x.{core::num::+}(1){(core::num) → core::num}) is core::int) {
77+
x{core::int}.{core::int::isEven}{core::bool};
78+
}
79+
}
80+
static method is_postfix() → dynamic {
81+
core::num x = 2;
82+
if((let final core::num #t2 = x in let final core::num #t3 = x = #t2.{core::num::+}(1){(core::num) → core::num} in #t2) is core::int) {
83+
invalid-expression "pkg/front_end/testcases/inference_update_4/assignment_promotion_in_if_statement.dart:44:7: Error: The getter 'isEven' isn't defined for the class 'num'.
84+
Try correcting the name to the name of an existing getter, or defining a getter or field named 'isEven'.
85+
x.isEven; // Error.
86+
^^^^^^" in x{<unresolved>}.isEven;
87+
}
88+
}
89+
static method is_prefix() → dynamic {
90+
core::num x = 2;
91+
if((x = x.{core::num::+}(1){(core::num) → core::num}) is core::int) {
92+
x{core::int}.{core::int::isEven}{core::bool};
93+
}
94+
}

0 commit comments

Comments
 (0)