Skip to content

Commit 001e0e6

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[model] Make null-guarded lowerings of cascades type safe
Change-Id: I035380abdb951a48110029ae93f965ad8bf83093 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420100 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent 94eb611 commit 001e0e6

File tree

5 files changed

+52
-14
lines changed

5 files changed

+52
-14
lines changed

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

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -393,11 +393,48 @@ class NullAwareGuard {
393393
_inferrer.flowAnalysis.nullAwareAccess_end();
394394
Expression equalsNull = _inferrer.createEqualsNull(
395395
_nullAwareFileOffset, createVariableGet(_nullAwareVariable));
396+
397+
// In case null guards are applied to non-nullable receivers, we still
398+
// generate a null-testing conditional expression; although, it is
399+
// unnecessary. Moreover, those expressions appear to be not null safe,
400+
// since their static type is not nullable, and one of their branches is the
401+
// `null` literal. The following is an example of such lowering for the
402+
// expression `s?..length`, where `s` is of static type `String`.
403+
//
404+
// let final core::String #t1 = s in #t1 == null ?{core::String}
405+
// null :
406+
// block { #t1.{core::String::length}{core::int}; } =>#t1
407+
//
408+
// Note the static type of the condition expression being the non-nullable
409+
// `core::String`, and the then-branch being `null`. In such cases, we
410+
// implement the following workaround: replace the `null` literal with the
411+
// expression tested for being `null`. With the workaround in place, the
412+
// expression `s?..length` is lowered as follows:
413+
//
414+
// let final core::String #t1 = s in #t1 == null ?{core::String}
415+
// #t1 :
416+
// block { #t1.{core::String::length}{core::int}; } =>#t1
417+
//
418+
// This achieves the following:
419+
//
420+
// * The conditional expression becomes type-safe.
421+
// * Semantically the tested expression and the null literal evaluate to
422+
// the same result, to the `null` value, so the runtime properties of
423+
// the code don't change.
424+
// * In practice, the `null` literal is dead code for non-nullable
425+
// receivers anyway, so the altered part of the expression won't ever be
426+
// executed.
427+
//
428+
// TODO(johnniwinther,cstefantsova): Don't generate null-testing expressions
429+
// for non-nullable receivers in cascades.
430+
Expression typeSafeIfNullBranch =
431+
inferredType.nullability == Nullability.nullable
432+
? new NullLiteral()
433+
: createVariableGet(_nullAwareVariable);
434+
typeSafeIfNullBranch.fileOffset = _nullAwareFileOffset;
435+
396436
ConditionalExpression condition = new ConditionalExpression(
397-
equalsNull,
398-
new NullLiteral()..fileOffset = _nullAwareFileOffset,
399-
nullAwareAction,
400-
inferredType)
437+
equalsNull, typeSafeIfNullBranch, nullAwareAction, inferredType)
401438
..fileOffset = _nullAwareFileOffset;
402439
return new Let(_nullAwareVariable, condition)
403440
..fileOffset = _nullAwareFileOffset;

pkg/front_end/test/spell_checking_list_common.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ accurate
5252
accurately
5353
achieve
5454
achieved
55+
achieves
5556
act
5657
acting
5758
action

pkg/front_end/testcases/nnbd/strictly_non_nullable_warnings.dart.strong.expect

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static extension-member method E|[](lowered final core::String #this, core::int
3131
return 42;
3232
static method warning(core::String s, core::List<core::String> l, core::Map<core::String, core::int> m) → dynamic {
3333
let final core::String #t2 = s in #t2 == null ?{core::int?} null : #t2.{core::String::length}{core::int};
34-
let final core::String #t3 = s in #t3 == null ?{core::String} null : block {
34+
let final core::String #t3 = s in #t3 == null ?{core::String} #t3 : block {
3535
#t3.{core::String::length}{core::int};
3636
} =>#t3;
3737
let final core::String #t4 = s in #t4 == null ?{core::String} "foo" : #t4;
@@ -75,10 +75,10 @@ static method warning(core::String s, core::List<core::String> l, core::Map<core
7575
let final core::String #t20 = s in let final core::int #t21 = 42 in self::E|[](#t20, #t21) == null ?{core::int?} self::E|[]=(#t20, #t21, 42) : null;
7676
let final core::List<core::String> #t22 = l in let final core::int #t23 = 42 in #t22.{core::List::[]}(#t23){(core::int) → core::String} == null ?{core::String?} #t22.{core::List::[]=}(#t23, "foo"){(core::int, core::String) → void} : null;
7777
let final core::List<core::String> #t24 = l in #t24.{core::List::length}{core::int} == null ?{core::int?} #t24.{core::List::length} = 42 : null;
78-
let final core::List<core::String> #t25 = l in #t25 == null ?{core::List<core::String>} null : block {
78+
let final core::List<core::String> #t25 = l in #t25 == null ?{core::List<core::String>} #t25 : block {
7979
#t25.{core::List::length} = 42;
8080
} =>#t25;
81-
let final core::List<core::String> #t26 = l in #t26 == null ?{core::List<core::String>} null : block {
81+
let final core::List<core::String> #t26 = l in #t26 == null ?{core::List<core::String>} #t26 : block {
8282
let final core::List<core::String> #t27 = #t26 in #t27.{core::List::length}{core::int} == null ?{core::int?} #t27.{core::List::length} = 42 : null;
8383
} =>#t26;
8484
}

pkg/front_end/testcases/nnbd/strictly_non_nullable_warnings.dart.strong.modular.expect

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static extension-member method E|[](lowered final core::String #this, core::int
3131
return 42;
3232
static method warning(core::String s, core::List<core::String> l, core::Map<core::String, core::int> m) → dynamic {
3333
let final core::String #t2 = s in #t2 == null ?{core::int?} null : #t2.{core::String::length}{core::int};
34-
let final core::String #t3 = s in #t3 == null ?{core::String} null : block {
34+
let final core::String #t3 = s in #t3 == null ?{core::String} #t3 : block {
3535
#t3.{core::String::length}{core::int};
3636
} =>#t3;
3737
let final core::String #t4 = s in #t4 == null ?{core::String} "foo" : #t4;
@@ -75,10 +75,10 @@ static method warning(core::String s, core::List<core::String> l, core::Map<core
7575
let final core::String #t20 = s in let final core::int #t21 = 42 in self::E|[](#t20, #t21) == null ?{core::int?} self::E|[]=(#t20, #t21, 42) : null;
7676
let final core::List<core::String> #t22 = l in let final core::int #t23 = 42 in #t22.{core::List::[]}(#t23){(core::int) → core::String} == null ?{core::String?} #t22.{core::List::[]=}(#t23, "foo"){(core::int, core::String) → void} : null;
7777
let final core::List<core::String> #t24 = l in #t24.{core::List::length}{core::int} == null ?{core::int?} #t24.{core::List::length} = 42 : null;
78-
let final core::List<core::String> #t25 = l in #t25 == null ?{core::List<core::String>} null : block {
78+
let final core::List<core::String> #t25 = l in #t25 == null ?{core::List<core::String>} #t25 : block {
7979
#t25.{core::List::length} = 42;
8080
} =>#t25;
81-
let final core::List<core::String> #t26 = l in #t26 == null ?{core::List<core::String>} null : block {
81+
let final core::List<core::String> #t26 = l in #t26 == null ?{core::List<core::String>} #t26 : block {
8282
let final core::List<core::String> #t27 = #t26 in #t27.{core::List::length}{core::int} == null ?{core::int?} #t27.{core::List::length} = 42 : null;
8383
} =>#t26;
8484
}

pkg/front_end/testcases/nnbd/strictly_non_nullable_warnings.dart.strong.transformed.expect

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static extension-member method E|[](lowered final core::String #this, core::int
3131
return 42;
3232
static method warning(core::String s, core::List<core::String> l, core::Map<core::String, core::int> m) → dynamic {
3333
let final core::String #t2 = s in #t2 == null ?{core::int?} null : #t2.{core::String::length}{core::int};
34-
let final core::String #t3 = s in #t3 == null ?{core::String} null : block {
34+
let final core::String #t3 = s in #t3 == null ?{core::String} #t3 : block {
3535
#t3.{core::String::length}{core::int};
3636
} =>#t3;
3737
let final core::String #t4 = s in #t4 == null ?{core::String} "foo" : #t4;
@@ -75,10 +75,10 @@ static method warning(core::String s, core::List<core::String> l, core::Map<core
7575
let final core::String #t20 = s in let final core::int #t21 = 42 in self::E|[](#t20, #t21) == null ?{core::int?} self::E|[]=(#t20, #t21, 42) : null;
7676
let final core::List<core::String> #t22 = l in let final core::int #t23 = 42 in #t22.{core::List::[]}(#t23){(core::int) → core::String} == null ?{core::String?} #t22.{core::List::[]=}(#t23, "foo"){(core::int, core::String) → void} : null;
7777
let final core::List<core::String> #t24 = l in #t24.{core::List::length}{core::int} == null ?{core::int?} #t24.{core::List::length} = 42 : null;
78-
let final core::List<core::String> #t25 = l in #t25 == null ?{core::List<core::String>} null : block {
78+
let final core::List<core::String> #t25 = l in #t25 == null ?{core::List<core::String>} #t25 : block {
7979
#t25.{core::List::length} = 42;
8080
} =>#t25;
81-
let final core::List<core::String> #t26 = l in #t26 == null ?{core::List<core::String>} null : block {
81+
let final core::List<core::String> #t26 = l in #t26 == null ?{core::List<core::String>} #t26 : block {
8282
let final core::List<core::String> #t27 = #t26 in #t27.{core::List::length}{core::int} == null ?{core::int?} #t27.{core::List::length} = 42 : null;
8383
} =>#t26;
8484
}
@@ -92,4 +92,4 @@ Evaluated: VariableGet @ org-dartlang-testcase:///strictly_non_nullable_warnings
9292
Evaluated: VariableGet @ org-dartlang-testcase:///strictly_non_nullable_warnings.dart:42:8 -> IntConstant(42)
9393
Evaluated: VariableGet @ org-dartlang-testcase:///strictly_non_nullable_warnings.dart:43:5 -> IntConstant(42)
9494
Evaluated: VariableGet @ org-dartlang-testcase:///strictly_non_nullable_warnings.dart:43:5 -> IntConstant(42)
95-
Extra constant evaluation: evaluated: 175, effectively constant: 6
95+
Extra constant evaluation: evaluated: 178, effectively constant: 6

0 commit comments

Comments
 (0)