blob: 4d81dcf861eef6cc5342ed224e1fd1d5c3d917f4 [file] [log] [blame]
// Copyright (c) 2019, 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 "package:expect/expect.dart" show Expect;
abstract class SubtypeTest<T, E> {
void isSubtype(String subtypeString, String supertypeString,
{bool legacyMode: false, String typeParameters}) {
E environment = extend(typeParameters);
T subtype = toType(subtypeString, environment);
T supertype = toType(supertypeString, environment);
String mode = legacyMode ? " (legacy)" : "";
Expect.isTrue(isSubtypeImpl(subtype, supertype, legacyMode),
"$subtypeString should be a subtype of $supertypeString$mode.");
}
void isNotSubtype(String subtypeString, String supertypeString,
{bool legacyMode: false, String typeParameters}) {
E environment = extend(typeParameters);
T subtype = toType(subtypeString, environment);
T supertype = toType(supertypeString, environment);
String mode = legacyMode ? " (legacy)" : "";
Expect.isFalse(isSubtypeImpl(subtype, supertype, legacyMode),
"$subtypeString shouldn't be a subtype of $supertypeString$mode.");
}
T toType(String text, E environment);
bool isSubtypeImpl(T subtype, T supertype, bool legacyMode);
E extend(String typeParameters);
void run() {
isSubtype('int', 'num', legacyMode: true);
isSubtype('int', 'Comparable<num>', legacyMode: true);
isSubtype('int', 'Comparable<Object>', legacyMode: true);
isSubtype('int', 'Object', legacyMode: true);
isSubtype('double', 'num', legacyMode: true);
isNotSubtype('int', 'double', legacyMode: true);
isNotSubtype('int', 'Comparable<int>', legacyMode: true);
isNotSubtype('int', 'Iterable<int>', legacyMode: true);
isNotSubtype('Comparable<int>', 'Iterable<int>', legacyMode: true);
isSubtype('List<int>', 'List<int>', legacyMode: true);
isSubtype('List<int>', 'Iterable<int>', legacyMode: true);
isSubtype('List<int>', 'List<num>', legacyMode: true);
isSubtype('List<int>', 'Iterable<num>', legacyMode: true);
isSubtype('List<int>', 'List<Object>', legacyMode: true);
isSubtype('List<int>', 'Iterable<Object>', legacyMode: true);
isSubtype('List<int>', 'Object', legacyMode: true);
isSubtype('List<int>', 'List<Comparable<Object>>', legacyMode: true);
isSubtype('List<int>', 'List<Comparable<num>>', legacyMode: true);
isSubtype('List<int>', 'List<Comparable<Comparable<num>>>',
legacyMode: true);
isNotSubtype('List<int>', 'List<double>', legacyMode: true);
isNotSubtype('List<int>', 'Iterable<double>', legacyMode: true);
isNotSubtype('List<int>', 'Comparable<int>', legacyMode: true);
isNotSubtype('List<int>', 'List<Comparable<int>>', legacyMode: true);
isNotSubtype('List<int>', 'List<Comparable<Comparable<int>>>',
legacyMode: true);
isSubtype('(num) -> num', '(int) -> num', legacyMode: true);
isSubtype('(num) -> int', '(num) -> num', legacyMode: true);
isSubtype('(num) -> int', '(int) -> num', legacyMode: true);
isNotSubtype('(int) -> int', '(num) -> num', legacyMode: true);
isSubtype('(num) -> (num) -> num', '(num) -> (int) -> num',
legacyMode: true);
isNotSubtype('(num) -> (int) -> int', '(num) -> (num) -> num',
legacyMode: true);
isSubtype('({num x}) -> num', '({int x}) -> num',
legacyMode: true); // named parameters
isSubtype('(num, {num x}) -> num', '(int, {int x}) -> num',
legacyMode: true);
isSubtype('({num x}) -> int', '({num x}) -> num', legacyMode: true);
isNotSubtype('({int x}) -> int', '({num x}) -> num', legacyMode: true);
isSubtype('<E>(E) -> int', '<E>(E) -> num',
legacyMode: true); // type parameters
isSubtype('<E>(num) -> E', '<E>(int) -> E', legacyMode: true);
isSubtype('<E>(E,num) -> E', '<E>(E,int) -> E', legacyMode: true);
isNotSubtype('<E>(E,num) -> E', '<E>(E,E) -> E', legacyMode: true);
isSubtype('<E>(E) -> (E) -> E', '<F>(F) -> (F) -> F', legacyMode: true);
isSubtype('<E>(E, (int,E) -> E) -> E', '<E>(E, (int,E) -> E) -> E',
legacyMode: true);
isSubtype('<E>(E, (int,E) -> E) -> E', '<E>(E, (num,E) -> E) -> E',
legacyMode: true);
isNotSubtype('<E,F>(E) -> (F) -> E', '<E>(E) -> <F>(F) -> E',
legacyMode: true);
isNotSubtype('<E,F>(E) -> (F) -> E', '<F,E>(E) -> (F) -> E',
legacyMode: true);
isNotSubtype('<E>(E,num) -> E', '<E extends num>(E,E) -> E',
legacyMode: true);
isNotSubtype('<E extends num>(E) -> int', '<E extends int>(E) -> int',
legacyMode: true);
isNotSubtype('<E extends num>(E) -> E', '<E extends int>(E) -> E',
legacyMode: true);
isNotSubtype('<E extends num>(int) -> E', '<E extends int>(int) -> E',
legacyMode: true);
isSubtype('<E extends num>(E) -> E', '<F extends num>(F) -> num',
legacyMode: true);
isSubtype('<E extends int>(E) -> E', '<F extends int>(F) -> num',
legacyMode: true);
isSubtype('<E extends int>(E) -> E', '<F extends int>(F) -> int',
legacyMode: true);
isNotSubtype('<E>(int) -> int', '(int) -> int', legacyMode: true);
isNotSubtype('<E,F>(int) -> int', '<E>(int) -> int', legacyMode: true);
isSubtype('<E extends List<E>>(E) -> E', '<F extends List<F>>(F) -> F',
legacyMode: true);
isNotSubtype(
'<E extends Iterable<E>>(E) -> E', '<F extends List<F>>(F) -> F',
legacyMode: true);
isNotSubtype('<E>(E,List<Object>) -> E', '<F extends List<F>>(F,F) -> F',
legacyMode: true);
isNotSubtype(
'<E>(E,List<Object>) -> List<E>', '<F extends List<F>>(F,F) -> F',
legacyMode: true);
isNotSubtype('<E>(E,List<Object>) -> int', '<F extends List<F>>(F,F) -> F',
legacyMode: true);
isNotSubtype('<E>(E,List<Object>) -> E', '<F extends List<F>>(F,F) -> void',
legacyMode: true);
isSubtype('int', 'FutureOr<int>');
isSubtype('int', 'FutureOr<num>');
isSubtype('Future<int>', 'FutureOr<int>');
isSubtype('Future<int>', 'FutureOr<num>');
isSubtype('Future<int>', 'FutureOr<Object>');
isSubtype('FutureOr<int>', 'FutureOr<int>');
isSubtype('FutureOr<int>', 'FutureOr<num>');
isSubtype('FutureOr<int>', 'Object');
isNotSubtype('int', 'FutureOr<double>');
isNotSubtype('FutureOr<double>', 'int');
isNotSubtype('FutureOr<int>', 'Future<num>');
isNotSubtype('FutureOr<int>', 'num');
// T & B <: T & A if B <: A
isSubtype('T & int', 'T & int', legacyMode: true, typeParameters: 'T');
isSubtype('T & int', 'T & num', legacyMode: true, typeParameters: 'T');
isSubtype('T & num', 'T & num', legacyMode: true, typeParameters: 'T');
isNotSubtype('T & num', 'T & int', legacyMode: true, typeParameters: 'T');
// T & B <: T extends A if B <: A
// (Trivially satisfied since promoted bounds are always a isSubtype of the
// original bound)
isSubtype('T & int', 'T',
legacyMode: true, typeParameters: 'T extends int');
isSubtype('T & int', 'T',
legacyMode: true, typeParameters: 'T extends num');
isSubtype('T & num', 'T',
legacyMode: true, typeParameters: 'T extends num');
// T extends B <: T & A if B <: A
isSubtype('T', 'T & int',
legacyMode: true, typeParameters: 'T extends int');
isSubtype('T', 'T & num',
legacyMode: true, typeParameters: 'T extends int');
isSubtype('T', 'T & num',
legacyMode: true, typeParameters: 'T extends num');
isNotSubtype('T', 'T & int',
legacyMode: true, typeParameters: 'T extends num');
// T extends A <: T extends A
isSubtype('T', 'T', legacyMode: true, typeParameters: 'T extends num');
// S & B <: A if B <: A, A is not S (or a promotion thereof)
isSubtype('S & int', 'int', legacyMode: true, typeParameters: 'S');
isSubtype('S & int', 'num', legacyMode: true, typeParameters: 'S');
isSubtype('S & num', 'num', legacyMode: true, typeParameters: 'S');
isNotSubtype('S & num', 'int', legacyMode: true, typeParameters: 'S');
isNotSubtype('S & num', 'T', legacyMode: true, typeParameters: 'S, T');
isNotSubtype('S & num', 'T & num',
legacyMode: true, typeParameters: 'S, T');
// S extends B <: A if B <: A, A is not S (or a promotion thereof)
isSubtype('S', 'int', legacyMode: true, typeParameters: 'S extends int');
isSubtype('S', 'num', legacyMode: true, typeParameters: 'S extends int');
isSubtype('S', 'num', legacyMode: true, typeParameters: 'S extends num');
isNotSubtype('S', 'int', legacyMode: true, typeParameters: 'S extends num');
isNotSubtype('S', 'T',
legacyMode: true, typeParameters: 'S extends num, T');
isNotSubtype('S', 'T & num',
legacyMode: true, typeParameters: 'S extends num, T');
}
}