Skip to content

Commit 2f3bca7

Browse files
chloestefantsovaCommit Queue
authored and
Commit Queue
committed
[cfe] Implement type inference for NullAwareElement
Part of #55955 Change-Id: Idbede4120a95ead2502779d271e8f6dc96059ab6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/377648 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent 3e58529 commit 2f3bca7

17 files changed

+375
-39
lines changed

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import 'package:kernel/ast.dart';
1919
import 'package:kernel/names.dart';
2020
import 'package:kernel/type_algebra.dart';
2121
import 'package:kernel/type_environment.dart';
22+
import 'package:kernel/src/non_null.dart';
2223

2324
import '../api_prototype/experimental_flags.dart';
2425
import '../api_prototype/lowering_predicates.dart';
@@ -2397,8 +2398,18 @@ class InferenceVisitorImpl extends InferenceVisitorBase
23972398
DartType inferredTypeArgument,
23982399
Map<TreeNode, DartType> inferredSpreadTypes,
23992400
Map<Expression, DartType> inferredConditionTypes) {
2400-
// TODO(cstefantsova): Implement type inference for null-aware elements.
2401-
return new ExpressionInferenceResult(const DynamicType(), element);
2401+
// TODO(cstefantsova): Ensure the flow analysis is properly invoked when it
2402+
// supports null-aware elements.
2403+
2404+
ExpressionInferenceResult expressionResult = inferElement(
2405+
element.expression,
2406+
inferredTypeArgument.withDeclaredNullability(Nullability.nullable),
2407+
inferredSpreadTypes,
2408+
inferredConditionTypes);
2409+
element.expression = expressionResult.expression..parent = element;
2410+
2411+
return new ExpressionInferenceResult(
2412+
computeNonNull(expressionResult.inferredType), element);
24022413
}
24032414

24042415
ExpressionInferenceResult _inferIfElement(
@@ -3313,7 +3324,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
33133324
// example, the null-aware element in the literal `<String>[?expr]` will be
33143325
// lowered into the following:
33153326
//
3316-
// String? #temp = expr as String?;
3327+
// String? #temp = expr;
33173328
// if (#temp != null) {
33183329
// #t.add(#temp{String});
33193330
// }
@@ -3322,17 +3333,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase
33223333
// `#temp{String}` represents the promotion of the variable `#temp` to the
33233334
// non-nullable type `String`.
33243335
//
3325-
// TODO(cstefantsova): Verify the static type of [element.expression] is
3326-
// checked by the type inference by now, and add an assert and remove the
3327-
// cast if that's so.
3336+
// Note that the type inference ensures that the static type of `expr` is a
3337+
// subtype of `String?`, and by that point we don't need to insert another
3338+
// cast to ensure it.
33283339

33293340
Expression value = element.expression;
33303341
DartType nullableElementType =
33313342
elementType.withDeclaredNullability(Nullability.nullable);
3332-
VariableDeclaration temp = _createVariable(
3333-
_createImplicitAs(
3334-
element.expression.fileOffset, value, nullableElementType),
3335-
nullableElementType);
3343+
VariableDeclaration temp = _createVariable(value, nullableElementType);
33363344
body.add(temp);
33373345

33383346
Statement statement = _createIf(

pkg/front_end/test/coverage_suite_expected.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -899,10 +899,10 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
899899
hitCount: 166,
900900
missCount: 0,
901901
),
902-
// 99.52038369304557%.
902+
// 99.54545454545455%.
903903
"package:front_end/src/type_inference/inference_visitor.dart": (
904-
hitCount: 7885,
905-
missCount: 38,
904+
hitCount: 7884,
905+
missCount: 36,
906906
),
907907
// 99.87525987525989%.
908908
"package:front_end/src/type_inference/inference_visitor_base.dart": (
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
test1(int? x) {
6+
var y1 = [?x];
7+
var y2 = [1, ?x];
8+
var y3 = [1.0, ?x];
9+
}
10+
11+
test2(dynamic x) {
12+
List<String> y1 = [?x];
13+
List<String> y2 = ["", ?x];
14+
var y3 = ["", ?x];
15+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test1(core::int? x) → dynamic {
6+
core::List<core::int> y1 = block {
7+
final core::List<core::int> #t1 = <core::int>[];
8+
final core::int? #t2 = x;
9+
if(!(#t2 == null))
10+
#t1.{core::List::add}{Invariant}(#t2{core::int}){(core::int) → void};
11+
} =>#t1;
12+
core::List<core::int> y2 = block {
13+
final core::List<core::int> #t3 = <core::int>[1];
14+
final core::int? #t4 = x;
15+
if(!(#t4 == null))
16+
#t3.{core::List::add}{Invariant}(#t4{core::int}){(core::int) → void};
17+
} =>#t3;
18+
core::List<core::num> y3 = block {
19+
final core::List<core::num> #t5 = <core::num>[1.0];
20+
final core::num? #t6 = x;
21+
if(!(#t6 == null))
22+
#t5.{core::List::add}{Invariant}(#t6{core::num}){(core::num) → void};
23+
} =>#t5;
24+
}
25+
static method test2(dynamic x) → dynamic {
26+
core::List<core::String> y1 = block {
27+
final core::List<core::String> #t7 = <core::String>[];
28+
final core::String? #t8 = x as{TypeError,ForDynamic} core::String?;
29+
if(!(#t8 == null))
30+
#t7.{core::List::add}{Invariant}(#t8{core::String}){(core::String) → void};
31+
} =>#t7;
32+
core::List<core::String> y2 = block {
33+
final core::List<core::String> #t9 = <core::String>[""];
34+
final core::String? #t10 = x as{TypeError,ForDynamic} core::String?;
35+
if(!(#t10 == null))
36+
#t9.{core::List::add}{Invariant}(#t10{core::String}){(core::String) → void};
37+
} =>#t9;
38+
core::List<dynamic> y3 = block {
39+
final core::List<dynamic> #t11 = <dynamic>[""];
40+
final dynamic #t12 = x;
41+
if(!(#t12 == null))
42+
#t11.{core::List::add}{Invariant}(#t12){(dynamic) → void};
43+
} =>#t11;
44+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test1(core::int? x) → dynamic {
6+
core::List<core::int> y1 = block {
7+
final core::List<core::int> #t1 = <core::int>[];
8+
final core::int? #t2 = x;
9+
if(!(#t2 == null))
10+
#t1.{core::List::add}{Invariant}(#t2{core::int}){(core::int) → void};
11+
} =>#t1;
12+
core::List<core::int> y2 = block {
13+
final core::List<core::int> #t3 = <core::int>[1];
14+
final core::int? #t4 = x;
15+
if(!(#t4 == null))
16+
#t3.{core::List::add}{Invariant}(#t4{core::int}){(core::int) → void};
17+
} =>#t3;
18+
core::List<core::num> y3 = block {
19+
final core::List<core::num> #t5 = <core::num>[1.0];
20+
final core::num? #t6 = x;
21+
if(!(#t6 == null))
22+
#t5.{core::List::add}{Invariant}(#t6{core::num}){(core::num) → void};
23+
} =>#t5;
24+
}
25+
static method test2(dynamic x) → dynamic {
26+
core::List<core::String> y1 = block {
27+
final core::List<core::String> #t7 = <core::String>[];
28+
final core::String? #t8 = x as{TypeError,ForDynamic} core::String?;
29+
if(!(#t8 == null))
30+
#t7.{core::List::add}{Invariant}(#t8{core::String}){(core::String) → void};
31+
} =>#t7;
32+
core::List<core::String> y2 = block {
33+
final core::List<core::String> #t9 = <core::String>[""];
34+
final core::String? #t10 = x as{TypeError,ForDynamic} core::String?;
35+
if(!(#t10 == null))
36+
#t9.{core::List::add}{Invariant}(#t10{core::String}){(core::String) → void};
37+
} =>#t9;
38+
core::List<dynamic> y3 = block {
39+
final core::List<dynamic> #t11 = <dynamic>[""];
40+
final dynamic #t12 = x;
41+
if(!(#t12 == null))
42+
#t11.{core::List::add}{Invariant}(#t12){(dynamic) → void};
43+
} =>#t11;
44+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test1(core::int? x) → dynamic
6+
;
7+
static method test2(dynamic x) → dynamic
8+
;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method test1(core::int? x) → dynamic {
6+
core::List<core::int> y1 = block {
7+
final core::List<core::int> #t1 = core::_GrowableList::•<core::int>(0);
8+
final core::int? #t2 = x;
9+
if(!(#t2 == null))
10+
#t1.{core::List::add}{Invariant}(#t2{core::int}){(core::int) → void};
11+
} =>#t1;
12+
core::List<core::int> y2 = block {
13+
final core::List<core::int> #t3 = core::_GrowableList::_literal1<core::int>(1);
14+
final core::int? #t4 = x;
15+
if(!(#t4 == null))
16+
#t3.{core::List::add}{Invariant}(#t4{core::int}){(core::int) → void};
17+
} =>#t3;
18+
core::List<core::num> y3 = block {
19+
final core::List<core::num> #t5 = core::_GrowableList::_literal1<core::num>(1.0);
20+
final core::num? #t6 = x;
21+
if(!(#t6 == null))
22+
#t5.{core::List::add}{Invariant}(#t6{core::num}){(core::num) → void};
23+
} =>#t5;
24+
}
25+
static method test2(dynamic x) → dynamic {
26+
core::List<core::String> y1 = block {
27+
final core::List<core::String> #t7 = core::_GrowableList::•<core::String>(0);
28+
final core::String? #t8 = x as{TypeError,ForDynamic} core::String?;
29+
if(!(#t8 == null))
30+
#t7.{core::List::add}{Invariant}(#t8{core::String}){(core::String) → void};
31+
} =>#t7;
32+
core::List<core::String> y2 = block {
33+
final core::List<core::String> #t9 = core::_GrowableList::_literal1<core::String>("");
34+
final core::String? #t10 = x as{TypeError,ForDynamic} core::String?;
35+
if(!(#t10 == null))
36+
#t9.{core::List::add}{Invariant}(#t10{core::String}){(core::String) → void};
37+
} =>#t9;
38+
core::List<dynamic> y3 = block {
39+
final core::List<dynamic> #t11 = core::_GrowableList::_literal1<dynamic>("");
40+
final dynamic #t12 = x;
41+
if(!(#t12 == null))
42+
#t11.{core::List::add}{Invariant}(#t12){(dynamic) → void};
43+
} =>#t11;
44+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
test1(int? x) {}
2+
3+
test2(dynamic x) {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
test1(int? x) {}
2+
3+
test2(dynamic x) {}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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/concurrent/concurrent.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
11+
//
12+
// sdk/lib/convert/convert.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
13+
//
14+
// sdk/lib/developer/developer.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
15+
//
16+
// sdk/lib/ffi/ffi.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
17+
//
18+
// sdk/lib/internal/internal.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
19+
//
20+
// sdk/lib/isolate/isolate.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
21+
//
22+
// sdk/lib/math/math.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
23+
//
24+
// sdk/lib/mirrors/mirrors.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
25+
//
26+
// 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.
27+
//
28+
// 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.
29+
//
30+
// sdk/lib/vmservice/vmservice.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
31+
//
32+
// 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.
33+
//
34+
// 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.
35+
//
36+
// sdk/lib/io/io.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
37+
//
38+
// sdk/lib/cli/cli.dart: Error: Loaded library is compiled with sound null safety and cannot be used in compilation for unsound null safety.
39+
//
40+
library;
41+
import self as self;
42+
import "dart:core" as core;
43+
44+
static method test1(core::int? x) → dynamic {
45+
core::List<core::int> y1 = block {
46+
final core::List<core::int> #t1 = <core::int>[];
47+
final core::int? #t2 = x;
48+
if(!(#t2 == null))
49+
#t1.{core::List::add}{Invariant}(#t2{core::int}){(core::int) → void};
50+
} =>#t1;
51+
core::List<core::int> y2 = block {
52+
final core::List<core::int> #t3 = <core::int>[1];
53+
final core::int? #t4 = x;
54+
if(!(#t4 == null))
55+
#t3.{core::List::add}{Invariant}(#t4{core::int}){(core::int) → void};
56+
} =>#t3;
57+
core::List<core::num> y3 = block {
58+
final core::List<core::num> #t5 = <core::num>[1.0];
59+
final core::num? #t6 = x;
60+
if(!(#t6 == null))
61+
#t5.{core::List::add}{Invariant}(#t6{core::num}){(core::num) → void};
62+
} =>#t5;
63+
}
64+
static method test2(dynamic x) → dynamic {
65+
core::List<core::String> y1 = block {
66+
final core::List<core::String> #t7 = <core::String>[];
67+
final core::String? #t8 = x as{TypeError,ForDynamic} core::String?;
68+
if(!(#t8 == null))
69+
#t7.{core::List::add}{Invariant}(#t8{core::String}){(core::String) → void};
70+
} =>#t7;
71+
core::List<core::String> y2 = block {
72+
final core::List<core::String> #t9 = <core::String>[""];
73+
final core::String? #t10 = x as{TypeError,ForDynamic} core::String?;
74+
if(!(#t10 == null))
75+
#t9.{core::List::add}{Invariant}(#t10{core::String}){(core::String) → void};
76+
} =>#t9;
77+
core::List<dynamic> y3 = block {
78+
final core::List<dynamic> #t11 = <dynamic>[""];
79+
final dynamic #t12 = x;
80+
if(!(#t12 == null))
81+
#t11.{core::List::add}{Invariant}(#t12){(dynamic) → void};
82+
} =>#t11;
83+
}

0 commit comments

Comments
 (0)