blob: f5cf5d4469c1ba8e38bceaf437beb09645bd20a9 [file] [log] [blame]
// Copyright (c) 2023, 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.
import "../../static_type_helper.dart";
test() {
{
// Flow analysis recognizes that `case null` is guaranteed to match an
// expression of type `Null`.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<Null>() case null) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int>>();
}
{
// In the general case, flow analysis assumes that a constant pattern may or
// may not match.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<Object?>() case 0) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// If `case null` does not match, the scrutinee is promoted to non-nullable.
int? reachability0 = 0;
int? reachability1 = 0;
var x = expr<int?>();
if (x case null) {
reachability0 = null;
// Note that in this branch, `x` is known to be `null`, but we don't
// promote to the type `Null`, for consistency with the flow analysis
// behavior of `== null`. See
// https://github.com/dart-lang/language/issues/1505#issuecomment-975706918
// for details.
x.expectStaticType<Exactly<int?>>();
} else {
reachability1 = null;
x.expectStaticType<Exactly<int>>();
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// `case null` does not promote the scrutinee if it's been changed by a
// previous case.
int? reachability0 = 0;
int? reachability1 = 0;
var x = expr<int?>();
switch (x) {
case _ when pickSecond(x = expr<int?>(), expr<bool>()):
break;
case null:
reachability0 = null;
x.expectStaticType<Exactly<int?>>();
case var y:
reachability1 = null;
x.expectStaticType<Exactly<int?>>();
y.expectStaticType<Exactly<int>>();
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// `case null` can even match non-nullable types.
// Note that in most cases, flow analysis assumes soundness when analyzing
// patterns. This is an exception: `case null` is assumed to be a possible
// match even for a non-nullable scrutinee; this makes `case null` behave
// similarly to an `if (x == null)` test.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<int>() case null) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// If a constant pattern appears inside a record pattern, the record's field
// type is not changed.
var x = expr<(Object,)>();
if (x case (1,)) {
// Note that at this point, we don't know that `x.$1` is an `int`; we just
// know that `1 == x.$1` returned `true`. (`x` could be `1.0`, for
// instance). In the general case where the constant value has a user
// defined type, we can't assume anything because the type might have a
// user-defined `==` operator. So for simplicity and consistency, we don't
// do any promotion for the case where a constant matches, regardless of
// the type of the constant.
x.expectStaticType<Exactly<(Object,)>>();
}
}
}
T expr<T>() => throw UnimplementedError();
T pickSecond<T>(dynamic x, T y) => y;
main() {}