blob: a3cc82074466d0dc5b95be1af443583747672740 [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.
// We say that a type T derives a future type F in the following cases,
// using the first applicable case:
//
// 1. If T is a type which is introduced by a class, mixin, or enum
// declaration, and if T or a direct or indirect superinterface of T
// is Future<U> for some U, then T derives the future type Future<U>.
// 2. If T is the type FutureOr<U> for some U, then T derives the future
// type FutureOr<U>.
// 3. If T is S? for some S, and S derives the future type F, then T derives
// the future type F?.
// 4. If T is a type variable with bound B, and B derives the future type F,
// then T derives the future type F.
// - (There is no rule for the case where T is of the form X&S because this
// will never occur.)
//
// In Dart of today, rule 1 is extended with a case for extension types.
//
// Many test cases below use an expression of the form `await e` where the
// static type of `e` is such that the rule in the definition of _flatten_
// which is used is one of the following:
//
// - If T derives a future type Future<S> or FutureOr<S> then flatten(T) = S.
// - If T derives a future type Future<S>? or FutureOr<S>? then flatten(T) = S?.
//
// This implies that the test which is concerned with the behavior of _flatten_
// is pretty directly also a test of the behavior of "derives a future type".
//
// To see that the "derives a future type" algorithm is applied correctly,
// we also need to check types of the form `X & B`, for which the "derives a
// future type" algorithm is applied to `B`. This kind of test is also needed
// for one more reason: If an implementation is not updated at all (so it
// uses the old definition of _flatten_) then it would succeed in many `T?`
// tests without ever invoking the "derives a future type" algorithm at all.
// With a type of the form `X & B`, both the old and the new definition must
// use "derives a future type".
//
// Comments of the form '// 3.2 ...' are used to indicate which rules are
// usde to find the future type which is derived: Rule 3 is used first, then
// a recursive invocation uses rule 2, and similarly for other digit
// sequences.
import 'dart:async';
import '../static_type_helper.dart';
mixin class FakeFutureHelper<T> {
Future<T> get _realFuture;
Future<R> then<R>(FutureOr<R> Function(T) onValue, {Function? onError}) =>
_realFuture.then<R>(onValue, onError: onError);
Future<T> catchError(Function onError, {bool Function(Object)? test}) =>
_realFuture.catchError(onError, test: test);
Future<T> whenComplete(FutureOr<void> Function() onDone) =>
_realFuture.whenComplete(onDone);
noSuchMethod(i) => throw UnimplementedError("Not needed for test");
}
class SubFuture extends FakeFutureHelper<int> implements Future<int> {
final Future<int> _realFuture = Future<int>.value(0);
}
mixin MixFuture implements Future<int> {
final Future<int> _realFuture = Future<int>.value(0);
}
class WithMixFuture extends Object with FakeFutureHelper<int>, MixFuture {
final Future<int> _realFuture = Future<int>.value(0);
}
enum EnumFuture with FakeFutureHelper<int> implements Future<int> {
one,
two;
Future<int> get _realFuture => this == one ? _futureOne : _futureTwo;
static final Future<int> _futureOne = Future<int>.value(1);
static final Future<int> _futureTwo = Future<int>.value(2);
}
// Note that the representation type is different from the superinterface.
extension type ExtensionFuture(Future<String> _) implements Future<String?> {}
extension type ExtensionSubFuture(SubFuture _) implements SubFuture {}
void main() async {
{
// 1, derives `Future<String>`.
var x = Future<String>.value("Whatever");
(await x).expectStaticType<Exactly<String>>();
}
{
// 1, derives `Future<int>`.
var x = SubFuture();
(await x).expectStaticType<Exactly<int>>();
}
{
// 1, derives `Future<int>`.
MixFuture x = WithMixFuture();
(await x).expectStaticType<Exactly<int>>();
}
{
// 1, derives `Future<int>`.
var x = EnumFuture.one;
(await x).expectStaticType<Exactly<int>>();
}
{
// 1, derives `Future<String?>`.
var x = ExtensionFuture(Future<String>.value("Whatever"));
(await x).expectStaticType<Exactly<String?>>();
}
{
// 1, derives `Future<int>`.
var x = ExtensionSubFuture(SubFuture());
(await x).expectStaticType<Exactly<int>>();
}
{
// 2, derives `FutureOr<bool>`.
FutureOr<bool> x = true;
(await x).expectStaticType<Exactly<bool>>();
}
{
// 2, derives `FutureOr<bool?>`.
FutureOr<bool?> x = true;
(await x).expectStaticType<Exactly<bool?>>();
}
{
// 3.1, derives `Future<String>?`.
Future<String>? x;
(await x).expectStaticType<Exactly<String?>>();
}
{
// 3.1, derives `Future<int>?`.
SubFuture? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.1, derives `Future<int>?`.
MixFuture? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.1, derives `Future<int>?`.
EnumFuture? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.1, derives `Future<String?>?`.
ExtensionFuture? x;
(await x).expectStaticType<Exactly<String?>>();
}
{
// 3.1, derives `Future<int>?`.
ExtensionSubFuture? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.2, derives `FutureOr<bool>?`.
FutureOr<bool>? x;
(await x).expectStaticType<Exactly<bool?>>();
}
{
// 3.2, derives `FutureOr<bool?>?`.
FutureOr<bool?>? x;
(await x).expectStaticType<Exactly<bool?>>();
}
await f1<Future<int>, SubFuture, ExtensionFuture, ExtensionSubFuture,
FutureOr<Object>, Future<Never>?, Future<Symbol>, Future<Symbol>>(
Future<int>.value(10),
SubFuture(),
ExtensionFuture(Future<String>.value('f1')),
ExtensionSubFuture(SubFuture()),
20,
null,
Future.value(#foo));
await f2<Object?, Future<Never>, FutureOr<Object>, Future<Never>?,
Future<Symbol>, Future<Symbol>>(true);
await C1<Future<int>, SubFuture, ExtensionFuture, ExtensionSubFuture,
FutureOr<Object>, Future<Never>?, Future<Symbol>, Future<Symbol>>()
.f1(
Future<int>.value(10),
SubFuture(),
ExtensionFuture(Future<String>.value('f1')),
ExtensionSubFuture(SubFuture()),
20,
null,
Future.value(#foo));
await C2<Object?, Future<Never>, FutureOr<Object>, Future<Never>?,
Future<Symbol>, Future<Symbol>>()
.f2(true);
}
// Test non-promoted types that involve function type parameters.
Future<void> f1<
X1a extends Future<int>,
X1b extends SubFuture,
X1c extends ExtensionFuture,
X1d extends ExtensionSubFuture,
X2 extends FutureOr<Object>,
X3 extends Future<Never>?,
X4 extends Y,
Y extends Future<Symbol>>(
X1a x1a, X1b x1b, X1c x1c, X1d x1d, X2 x2, X3 x3, X4 x4) async {
// 4.1, `X1a` derives `Future<int>`.
(await x1a).expectStaticType<Exactly<int>>();
// 4.1, `X1b` derives `Future<int>`.
(await x1b).expectStaticType<Exactly<int>>();
// 4.1, `X1c` derives `Future<String?>`.
(await x1c).expectStaticType<Exactly<String?>>();
// 4.1, `X1d` derives `Future<int>`.
(await x1d).expectStaticType<Exactly<int>>();
// 4.2, `X2` derives `FutureOr<Object>`.
(await x2).expectStaticType<Exactly<Object>>();
// 4.3.1, `X3` derives `Future<Never>?`.
(await x3).expectStaticType<Exactly<Never?>>();
// 4.4.1, `X4` derives `Future<Symbol>`.
(await x4).expectStaticType<Exactly<Symbol>>();
{
// 3.4.1, `X1a?` derives `Future<int>?`.
X1a? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.4.1, `X1b?` derives `Future<int>?`.
X1b? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.4.1, `X1c?` derives `Future<String?>?`.
X1c? x;
(await x).expectStaticType<Exactly<String?>>();
}
{
// 3.4.1, `X1d?` derives `Future<int>?`.
X1d? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.4.2, `X2?` derives `FutureOr<Object>?`.
X2? x;
(await x).expectStaticType<Exactly<Object?>>();
}
{
// 3.4.3.1, `X3?` derives `Future<Never>??`, i.e., `Future<Never>?`.
X3? x;
(await x).expectStaticType<Exactly<Never?>>();
}
{
// 3.4.4.1, `X4?` derives `Future<Symbol>?`.
X4? x;
(await x).expectStaticType<Exactly<Symbol?>>();
}
}
// Test promoted types that involve function type parameters. In this
// case the type of `x` is of the form `X & B`, which calls for the
// future type that `B` derives (if any). So the type of `x` may be
// `X & X1?`, but the relevant question is then: which future type does
// `X1?` derive?
Future<void> f2<
X,
X1 extends Future<Never>,
X2 extends FutureOr<Object>,
X3 extends Future<Never>?,
X4 extends Y,
Y extends Future<Symbol>>(X x) async {
if (x is X1) {
// 4.1, `X1` derives `Future<Never>`.
(await x).expectStaticType<Exactly<Never?>>();
}
if (x is X2) {
// 4.2, `X2` derives `FutureOr<Object>`.
(await x).expectStaticType<Exactly<Object>>();
}
if (x is X3) {
// 4.3.1, `X3` derives `Future<Never>?`.
(await x).expectStaticType<Exactly<Never?>>();
}
if (x is X4) {
// 4.4.1, `X4` derives `Future<Symbol>`.
(await x).expectStaticType<Exactly<Symbol>>();
}
if (x is X1?) {
// 3.4.1, `X1?` derives `Future<Never>?`.
(await x).expectStaticType<Exactly<Never?>>();
}
if (x is X2?) {
// 3.4.2, `X2?` derives `FutureOr<Object>?`.
(await x).expectStaticType<Exactly<Object?>>();
}
if (x is X3?) {
// 3.4.3.1, `X3?` derives `Future<Never>??`, i.e., `Future<Never>?`.
X3? x;
(await x).expectStaticType<Exactly<Never?>>();
}
if (x is X4?) {
// 3.4.4.1, `X4?` derives `Future<Symbol>?`.
(await x).expectStaticType<Exactly<Symbol?>>();
}
}
// Test non-promoted types that involve class type parameters.
class C1<
X1a extends Future<int>,
X1b extends SubFuture,
X1c extends ExtensionFuture,
X1d extends ExtensionSubFuture,
X2 extends FutureOr<Object>,
X3 extends Future<Never>?,
X4 extends Y,
Y extends Future<Symbol>> {
Future<void> f1(
X1a x1a, X1b x1b, X1c x1c, X1d x1d, X2 x2, X3 x3, X4 x4) async {
// 4.1, `X1a` derives `Future<int>`.
(await x1a).expectStaticType<Exactly<int>>();
// 4.1, `X1b` derives `Future<int>`.
(await x1b).expectStaticType<Exactly<int>>();
// 4.1, `X1c` derives `Future<String?>`.
(await x1c).expectStaticType<Exactly<String?>>();
// 4.1, `X1d` derives `Future<int>`.
(await x1d).expectStaticType<Exactly<int>>();
// 4.2, `X2` derives `FutureOr<Object>`.
(await x2).expectStaticType<Exactly<Object>>();
// 4.3.1, `X3` derives `Future<Never>?`.
(await x3).expectStaticType<Exactly<Never?>>();
// 4.4.1, `X4` derives `Future<Symbol>`.
(await x4).expectStaticType<Exactly<Symbol>>();
{
// 3.4.1, `X1a?` derives `Future<int>?`.
X1a? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.4.1, `X1b?` derives `Future<int>?`.
X1b? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.4.1, `X1c?` derives `Future<String?>?`.
X1c? x;
(await x).expectStaticType<Exactly<String?>>();
}
{
// 3.4.1, `X1d?` derives `Future<int>?`.
X1d? x;
(await x).expectStaticType<Exactly<int?>>();
}
{
// 3.4.2, `X2?` derives `FutureOr<Object>?`.
X2? x;
(await x).expectStaticType<Exactly<Object?>>();
}
{
// 3.4.3.1, `X3?` derives `Future<Never>??`, i.e., `Future<Never>?`.
X3? x;
(await x).expectStaticType<Exactly<Never?>>();
}
{
// 3.4.4.1, `X4?` derives `Future<Symbol>?`.
X4? x;
(await x).expectStaticType<Exactly<Symbol?>>();
}
}
}
// Test promoted types that involve type parameters. In this case the
// type of `x` is of the form `X & B`, which calls for the future type
// that `B` derives (if any). So the type of `x` may be `X & X1?`, but
// the relevant question is then: which future type does `X1?` derive?
class C2<X, X1 extends Future<Never>, X2 extends FutureOr<Object>,
X3 extends Future<Never>?, X4 extends Y, Y extends Future<Symbol>> {
Future<void> f2(X x) async {
if (x is X1) {
// 4.1, `X1` derives `Future<Never>`.
(await x).expectStaticType<Exactly<Never?>>();
}
if (x is X2) {
// 4.2, `X2` derives `FutureOr<Object>`.
(await x).expectStaticType<Exactly<Object>>();
}
if (x is X3) {
// 4.3.1, `X3` derives `Future<Never>?`.
(await x).expectStaticType<Exactly<Never?>>();
}
if (x is X4) {
// 4.4.1, `X4` derives `Future<Symbol>`.
(await x).expectStaticType<Exactly<Symbol>>();
}
if (x is X1?) {
// 3.4.1, `X1?` derives `Future<Never>?`.
(await x).expectStaticType<Exactly<Never?>>();
}
if (x is X2?) {
// 3.4.2, `X2?` derives `FutureOr<Object>?`.
(await x).expectStaticType<Exactly<Object?>>();
}
if (x is X3?) {
// 3.4.3.1, `X3?` derives `Future<Never>??`, i.e., `Future<Never>?`.
X3? x;
(await x).expectStaticType<Exactly<Never?>>();
}
if (x is X4?) {
// 3.4.4.1, `X4?` derives `Future<Symbol>?`.
(await x).expectStaticType<Exactly<Symbol?>>();
}
}
}