Skip to content

Commit c92712d

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[cfe] Use the null-aware flag in upwards inference in map literals
Part of #55955 Change-Id: I73171619ee33b13087cc99aee8489c9fb27a445b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/390700 Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]> Reviewed-by: Johnni Winther <[email protected]>
1 parent d373eef commit c92712d

10 files changed

+202
-4
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4607,8 +4607,12 @@ class InferenceVisitorImpl extends InferenceVisitorBase
46074607
.expression;
46084608
entry.value = value..parent = entry;
46094609

4610-
actualTypes.add(computeNonNull(keyInferenceResult.inferredType));
4611-
actualTypes.add(computeNonNull(valueInferenceResult.inferredType));
4610+
actualTypes.add(entry.isKeyNullAware
4611+
? computeNonNull(keyInferenceResult.inferredType)
4612+
: keyInferenceResult.inferredType);
4613+
actualTypes.add(entry.isValueNullAware
4614+
? computeNonNull(valueInferenceResult.inferredType)
4615+
: valueInferenceResult.inferredType);
46124616
actualTypesForSet.add(const DynamicType());
46134617

46144618
offsets.mapEntryOffset = entry.fileOffset;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
test(String? key, num? value) {
6+
var map1 = {key: value};
7+
var map2 = {?key: value};
8+
var map3 = {key: ?value};
9+
var map4 = {?key: ?value};
10+
11+
map1.expectStaticType<Exactly<Map<String?, num?>>>();
12+
map2.expectStaticType<Exactly<Map<String, num?>>>();
13+
map3.expectStaticType<Exactly<Map<String?, num>>>();
14+
map4.expectStaticType<Exactly<Map<String, num>>>();
15+
}
16+
17+
typedef Exactly<T> = T Function(T);
18+
19+
extension E<X> on X {
20+
void expectStaticType<Y extends Exactly<X>>() {}
21+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
typedef Exactly<invariant T extends core::Object? = dynamic> = (T%) → T%;
6+
extension E<X extends core::Object? = dynamic> on X% {
7+
method expectStaticType = self::E|expectStaticType;
8+
method tearoff expectStaticType = self::E|get#expectStaticType;
9+
}
10+
static method test(core::String? key, core::num? value) → dynamic {
11+
core::Map<core::String?, core::num?> map1 = <core::String?, core::num?>{key: value};
12+
core::Map<core::String, core::num?> map2 = block {
13+
final core::Map<core::String, core::num?> #t1 = <core::String, core::num?>{};
14+
final core::String? #t2 = key;
15+
if(!(#t2 == null))
16+
#t1.{core::Map::[]=}{Invariant}(#t2{core::String}, value){(core::String, core::num?) → void};
17+
} =>#t1;
18+
core::Map<core::String?, core::num> map3 = block {
19+
final core::Map<core::String?, core::num> #t3 = <core::String?, core::num>{};
20+
final core::num? #t4 = value;
21+
if(!(#t4 == null))
22+
#t3.{core::Map::[]=}{Invariant}(key, #t4{core::num}){(core::String?, core::num) → void};
23+
} =>#t3;
24+
core::Map<core::String, core::num> map4 = block {
25+
final core::Map<core::String, core::num> #t5 = <core::String, core::num>{};
26+
final core::String? #t6 = key;
27+
if(!(#t6 == null)) {
28+
final core::num? #t7 = value;
29+
if(!(#t7 == null))
30+
#t5.{core::Map::[]=}{Invariant}(#t6{core::String}, #t7{core::num}){(core::String, core::num) → void};
31+
}
32+
} =>#t5;
33+
self::E|expectStaticType<core::Map<core::String?, core::num?>, (core::Map<core::String?, core::num?>) → core::Map<core::String?, core::num?>>(map1);
34+
self::E|expectStaticType<core::Map<core::String, core::num?>, (core::Map<core::String, core::num?>) → core::Map<core::String, core::num?>>(map2);
35+
self::E|expectStaticType<core::Map<core::String?, core::num>, (core::Map<core::String?, core::num>) → core::Map<core::String?, core::num>>(map3);
36+
self::E|expectStaticType<core::Map<core::String, core::num>, (core::Map<core::String, core::num>) → core::Map<core::String, core::num>>(map4);
37+
}
38+
static extension-member method E|expectStaticType<X extends core::Object? = dynamic, Y extends (self::E|expectStaticType::X%) → self::E|expectStaticType::X% = (dynamic) → dynamic>(lowered final self::E|expectStaticType::X% #this) → void {}
39+
static extension-member method E|get#expectStaticType<X extends core::Object? = dynamic>(lowered final self::E|get#expectStaticType::X% #this) → <Y extends (self::E|get#expectStaticType::X%) → self::E|get#expectStaticType::X% = (dynamic) → dynamic>() → void
40+
return <Y extends (self::E|get#expectStaticType::X%) → self::E|get#expectStaticType::X% = (dynamic) → dynamic>() → void => self::E|expectStaticType<self::E|get#expectStaticType::X%, Y>(#this);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
typedef Exactly<invariant T extends core::Object? = dynamic> = (T%) → T%;
6+
extension E<X extends core::Object? = dynamic> on X% {
7+
method expectStaticType = self::E|expectStaticType;
8+
method tearoff expectStaticType = self::E|get#expectStaticType;
9+
}
10+
static method test(core::String? key, core::num? value) → dynamic {
11+
core::Map<core::String?, core::num?> map1 = <core::String?, core::num?>{key: value};
12+
core::Map<core::String, core::num?> map2 = block {
13+
final core::Map<core::String, core::num?> #t1 = <core::String, core::num?>{};
14+
final core::String? #t2 = key;
15+
if(!(#t2 == null))
16+
#t1.{core::Map::[]=}{Invariant}(#t2{core::String}, value){(core::String, core::num?) → void};
17+
} =>#t1;
18+
core::Map<core::String?, core::num> map3 = block {
19+
final core::Map<core::String?, core::num> #t3 = <core::String?, core::num>{};
20+
final core::num? #t4 = value;
21+
if(!(#t4 == null))
22+
#t3.{core::Map::[]=}{Invariant}(key, #t4{core::num}){(core::String?, core::num) → void};
23+
} =>#t3;
24+
core::Map<core::String, core::num> map4 = block {
25+
final core::Map<core::String, core::num> #t5 = <core::String, core::num>{};
26+
final core::String? #t6 = key;
27+
if(!(#t6 == null)) {
28+
final core::num? #t7 = value;
29+
if(!(#t7 == null))
30+
#t5.{core::Map::[]=}{Invariant}(#t6{core::String}, #t7{core::num}){(core::String, core::num) → void};
31+
}
32+
} =>#t5;
33+
self::E|expectStaticType<core::Map<core::String?, core::num?>, (core::Map<core::String?, core::num?>) → core::Map<core::String?, core::num?>>(map1);
34+
self::E|expectStaticType<core::Map<core::String, core::num?>, (core::Map<core::String, core::num?>) → core::Map<core::String, core::num?>>(map2);
35+
self::E|expectStaticType<core::Map<core::String?, core::num>, (core::Map<core::String?, core::num>) → core::Map<core::String?, core::num>>(map3);
36+
self::E|expectStaticType<core::Map<core::String, core::num>, (core::Map<core::String, core::num>) → core::Map<core::String, core::num>>(map4);
37+
}
38+
static extension-member method E|expectStaticType<X extends core::Object? = dynamic, Y extends (self::E|expectStaticType::X%) → self::E|expectStaticType::X% = (dynamic) → dynamic>(lowered final self::E|expectStaticType::X% #this) → void {}
39+
static extension-member method E|get#expectStaticType<X extends core::Object? = dynamic>(lowered final self::E|get#expectStaticType::X% #this) → <Y extends (self::E|get#expectStaticType::X%) → self::E|get#expectStaticType::X% = (dynamic) → dynamic>() → void
40+
return <Y extends (self::E|get#expectStaticType::X%) → self::E|get#expectStaticType::X% = (dynamic) → dynamic>() → void => self::E|expectStaticType<self::E|get#expectStaticType::X%, Y>(#this);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
typedef Exactly<invariant T extends core::Object? = dynamic> = (T%) → T%;
6+
extension E<X extends core::Object? = dynamic> on X% {
7+
method expectStaticType = self::E|expectStaticType;
8+
method tearoff expectStaticType = self::E|get#expectStaticType;
9+
}
10+
static method test(core::String? key, core::num? value) → dynamic
11+
;
12+
static extension-member method E|expectStaticType<X extends core::Object? = dynamic, Y extends (self::E|expectStaticType::X%) → self::E|expectStaticType::X% = (dynamic) → dynamic>(lowered final self::E|expectStaticType::X% #this) → void
13+
;
14+
static extension-member method E|get#expectStaticType<X extends core::Object? = dynamic>(lowered final self::E|get#expectStaticType::X% #this) → <Y extends (self::E|get#expectStaticType::X%) → self::E|get#expectStaticType::X% = (dynamic) → dynamic>() → void
15+
return <Y extends (self::E|get#expectStaticType::X%) → self::E|get#expectStaticType::X% = (dynamic) → dynamic>() → void => self::E|expectStaticType<self::E|get#expectStaticType::X%, Y>(#this);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
typedef Exactly<invariant T extends core::Object? = dynamic> = (T%) → T%;
6+
extension E<X extends core::Object? = dynamic> on X% {
7+
method expectStaticType = self::E|expectStaticType;
8+
method tearoff expectStaticType = self::E|get#expectStaticType;
9+
}
10+
static method test(core::String? key, core::num? value) → dynamic {
11+
core::Map<core::String?, core::num?> map1 = <core::String?, core::num?>{key: value};
12+
core::Map<core::String, core::num?> map2 = block {
13+
final core::Map<core::String, core::num?> #t1 = <core::String, core::num?>{};
14+
final core::String? #t2 = key;
15+
if(!(#t2 == null))
16+
#t1.{core::Map::[]=}{Invariant}(#t2{core::String}, value){(core::String, core::num?) → void};
17+
} =>#t1;
18+
core::Map<core::String?, core::num> map3 = block {
19+
final core::Map<core::String?, core::num> #t3 = <core::String?, core::num>{};
20+
final core::num? #t4 = value;
21+
if(!(#t4 == null))
22+
#t3.{core::Map::[]=}{Invariant}(key, #t4{core::num}){(core::String?, core::num) → void};
23+
} =>#t3;
24+
core::Map<core::String, core::num> map4 = block {
25+
final core::Map<core::String, core::num> #t5 = <core::String, core::num>{};
26+
final core::String? #t6 = key;
27+
if(!(#t6 == null)) {
28+
final core::num? #t7 = value;
29+
if(!(#t7 == null))
30+
#t5.{core::Map::[]=}{Invariant}(#t6{core::String}, #t7{core::num}){(core::String, core::num) → void};
31+
}
32+
} =>#t5;
33+
self::E|expectStaticType<core::Map<core::String?, core::num?>, (core::Map<core::String?, core::num?>) → core::Map<core::String?, core::num?>>(map1);
34+
self::E|expectStaticType<core::Map<core::String, core::num?>, (core::Map<core::String, core::num?>) → core::Map<core::String, core::num?>>(map2);
35+
self::E|expectStaticType<core::Map<core::String?, core::num>, (core::Map<core::String?, core::num>) → core::Map<core::String?, core::num>>(map3);
36+
self::E|expectStaticType<core::Map<core::String, core::num>, (core::Map<core::String, core::num>) → core::Map<core::String, core::num>>(map4);
37+
}
38+
static extension-member method E|expectStaticType<X extends core::Object? = dynamic, Y extends (self::E|expectStaticType::X%) → self::E|expectStaticType::X% = (dynamic) → dynamic>(lowered final self::E|expectStaticType::X% #this) → void {}
39+
static extension-member method E|get#expectStaticType<X extends core::Object? = dynamic>(lowered final self::E|get#expectStaticType::X% #this) → <Y extends (self::E|get#expectStaticType::X%) → self::E|get#expectStaticType::X% = (dynamic) → dynamic>() → void
40+
return <Y extends (self::E|get#expectStaticType::X%) → self::E|get#expectStaticType::X% = (dynamic) → dynamic>() → void => self::E|expectStaticType<self::E|get#expectStaticType::X%, Y>(#this);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
test(String? key, num? value) {}
2+
3+
typedef Exactly<T> = T Function(T);
4+
5+
extension E<X> on X {
6+
void expectStaticType<Y extends Exactly<X>>() {}
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extension E<X> on X {
2+
void expectStaticType<Y extends Exactly<X>>() {}
3+
}
4+
5+
test(String? key, num? value) {}
6+
7+
typedef Exactly<T> = T Function(T);

tests/language/null_aware_elements/const_literals_error_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ const map2 = {?nullVar: 1, intConst: 1, stringConst: 1};
9494
const map3 = {null: ?nullVar, intConst: 1, stringConst: 1};
9595
// ^
9696
// [cfe] Constant evaluation error:
97-
// ^
98-
// [cfe] The value 'null' can't be assigned to a variable of type 'Object' because 'Object' is not nullable.
9997
// ^^^^^^^
10098
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
10199
// [analyzer] COMPILE_TIME_ERROR.NON_CONSTANT_MAP_VALUE
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
// SharedOptions=--enable-experiment=null-aware-elements
6+
7+
import '../static_type_helper.dart';
8+
9+
test(String? key, num? value) {
10+
var map1 = {key: value};
11+
var map2 = {?key: value};
12+
var map3 = {key: ?value};
13+
var map4 = {?key: ?value};
14+
15+
map1.expectStaticType<Exactly<Map<String?, num?>>>();
16+
map2.expectStaticType<Exactly<Map<String, num?>>>();
17+
map3.expectStaticType<Exactly<Map<String?, num>>>();
18+
map4.expectStaticType<Exactly<Map<String, num>>>();
19+
}
20+
21+
main() {
22+
test(null, null);
23+
test(null, 0);
24+
test("", null);
25+
test("", 0);
26+
}

0 commit comments

Comments
 (0)