blob: 7d9a668bdbb861721fcb8d3748d86bc95c0f0d84 [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() {
{
// If the matched value type is `Null`, an `== null` pattern is guaranteed
// to match.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<Null>() case == null) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int>>();
}
{
// An `== null` pattern inside a subpattern is guaranteed to match if the
// matched value type of the subpattern is `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, a relational pattern using `==` 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?>>();
}
{
// An `== null` pattern promotes a scrutinee in the case where it does *not*
// match.
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?>>();
}
{
// An `== null` pattern doesn't promote a scrutinee that has been changed in
// a previous guard clause.
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?>>();
}
{
// An `== null` pattern 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 relational pattern using `==` appears inside a record pattern, the
// record's field type is not changed.
var x = expr<(Object?,)>();
if (x case (== const Object(),)) {
x.expectStaticType<Exactly<(Object?,)>>();
}
}
{
// Even if the relational pattern is an `== null` pattern.
var x = expr<(Object?,)>();
if (x case (== null,)) {
x.expectStaticType<Exactly<(Object?,)>>();
}
}
{
// A `!= null` pattern cannot match the `Null` type.
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 general, a relational pattern using `!=` 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?>>();
}
{
// A `!= null` pattern promotes a scrutinee to non-nullable.
int? reachability0 = 0;
int? reachability1 = 0;
var x = expr<int?>();
if (x case != null) {
reachability0 = null;
x.expectStaticType<Exactly<int>>();
} else {
reachability1 = null;
x.expectStaticType<Exactly<int?>>();
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// A `!= null` pattern doesn't promote a scrutinee that has been changed in
// a previous guard clause.
int? reachability0 = 0;
var x = expr<int?>();
switch (x) {
case _ when pickSecond(x = expr<int?>(), expr<bool>()):
break;
case != null && var y:
reachability0 = null;
x.expectStaticType<Exactly<int?>>();
y.expectStaticType<Exactly<int>>();
}
reachability0.expectStaticType<Exactly<int?>>();
}
{
// A `!= null` pattern promotes the matched value.
if (expr<int?>() case != null && var x) {
x.expectStaticType<Exactly<int>>();
}
}
{
// A `!= null` pattern may or may not match, even if the matched value type
// if non-nullable.
// Note that in most cases, flow analysis assumes soundness when analyzing
// patterns. This is an exception: `case != null` is assumed to be a
// possible mismatch 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 relational pattern using `!=` appears inside a record pattern, the
// record's field type is not changed.
var x = expr<(Object?,)>();
if (x case (!= const Object(),)) {
x.expectStaticType<Exactly<(Object?,)>>();
}
}
{
// Even if the relational pattern is a `!= null` pattern.
var x = expr<(Object?,)>();
if (x case (!= null,)) {
x.expectStaticType<Exactly<(Object,)>>();
}
}
{
// A relational pattern using a non-equality operator doesn't have any flow
// analysis effects. To verify this, we use an extension method so that `<
// null` is a valid pattern; this ensures that we don't accidentally apply
// the flow analysis rules for `== null` or `!= 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?>>();
}
{
// If a relational pattern using a non-equality operator appears inside a
// record pattern, the record's field type is not changed.
var x = expr<(int,)>();
if (x case (> 0,)) {
x.expectStaticType<Exactly<(int,)>>();
}
}
}
T expr<T>() => throw UnimplementedError();
T pickSecond<T>(dynamic x, T y) => y;
extension on Null {
bool operator <(Object? _0) => throw UnimplementedError();
}
main() {}