blob: 7703adcccc4dd7c74205f3bce1de87530740ed77 [file] [log] [blame]
// Copyright (c) 2024, 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.
// Tests the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null expressions.
import '../static_type_helper.dart';
/// Ensures a context type of `_` for the operand, if no type argument is
/// supplied.
Object? contextUnknown<T>(T x) => x;
/// Ensures a context type of `Iterable<T>` for the operand, or `Iterable<_>` if
/// no type argument is supplied.
Object? contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>` for the operand, or `B1<_>` if no type
/// argument is supplied.
Object? contextB1<T>(B1<T> x) => x;
main() {
// - An if-null expression `e` of the form `e1 ?? e2` with context type K is
// analyzed as follows:
//
// - Let T1 be the type of `e1` inferred with context type K?.
{
// Check the context type of `e1`:
// - Where the context is established using a function call argument.
context<num?>((contextType(1)..expectStaticType<Exactly<num?>>()) ?? 2);
context<num>((contextType(1)..expectStaticType<Exactly<num?>>()) ?? 2);
// - Where the context is established using local variable promotion.
Object? o;
o = 0 as Object?;
if (o is num?) {
o = (contextType(1)..expectStaticType<Exactly<num?>>()) ?? 2;
}
o = 0 as Object?;
if (o is num) {
o = (contextType(1)..expectStaticType<Exactly<num?>>()) ?? 2;
}
}
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If K is `_`, J = T1.
{
// Check the context type of `e2`.
var string = '';
var stringQuestion = null as String?;
contextUnknown(
// ignore: dead_null_aware_expression
string ?? (contextType('')..expectStaticType<Exactly<String>>()));
contextUnknown(stringQuestion ??
(contextType('')..expectStaticType<Exactly<String?>>()));
}
// - Otherwise, J = K.
{
var intQuestion = null as int?;
context<num?>(
intQuestion ?? (contextType(2)..expectStaticType<Exactly<num?>>()));
context<num>(
intQuestion ?? (contextType(2)..expectStaticType<Exactly<num>>()));
}
// - Let T be UP(NonNull(T1), T2).
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// This example has:
// - K = Object
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var intQuestion = null as int?;
var d = 2.0;
context<Object>((intQuestion ?? d)..expectStaticType<Exactly<num>>());
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2 = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableIntQuestion = null as Iterable<int>?;
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((iterableIntQuestion ?? iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, if NonNull(T1) <: S and T2 <: S, then the type of `e` is S.
{
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2 = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <: S
// Therefore the type of `e` is S = B1<Object?>.
var c1IntQuestion = null as C1<int>?;
var c2Double = C2<double>();
contextB1(
(c1IntQuestion ?? c2Double)..expectStaticType<Exactly<B1<Object?>>>());
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2 = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>(
(c1IntQuestion ?? c2Double)..expectStaticType<Exactly<B1<Object>>>());
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2 = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <: S
// Therefore the type of `e` is S = Iterable<num>.
var iterableIntQuestion = null as Iterable<int>?;
var listNum = <num>[];
context<Iterable<num>>((iterableIntQuestion ?? listNum)
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, the type of `e` is T.
{
var intQuestion = null as int?;
var d = 2.0;
Object? o;
var doubleQuestion = null as double?;
o = 0 as Object?;
if (o is int?) {
// This example has:
// - K = int?
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <!: S
// The fact that T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (intQuestion ?? d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// This example has:
// - K = int?
// - T1 = double?
// - T2 = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2 <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (doubleQuestion ?? intQuestion)..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// This example has:
// - K = String?
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2 <!: S
// The fact that NonNull(T1) <!: S and T2 <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (intQuestion ?? d)..expectStaticType<Exactly<num>>();
}
}
}