blob: 4a01bd29ef6712865303a4effa801e46f9c5e1b6 [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,
{String typeParameters}) {
E environment = extend(typeParameters);
T subtype = toType(subtypeString, environment);
T supertype = toType(supertypeString, environment);
Expect.isTrue(isSubtypeImpl(subtype, supertype),
"$subtypeString should be a subtype of $supertypeString.");
}
void isNotSubtype(String subtypeString, String supertypeString,
{String typeParameters}) {
E environment = extend(typeParameters);
T subtype = toType(subtypeString, environment);
T supertype = toType(supertypeString, environment);
Expect.isFalse(isSubtypeImpl(subtype, supertype),
"$subtypeString shouldn't be a subtype of $supertypeString.");
}
bool get skipFutureOrPromotion => false;
T toType(String text, E environment);
bool isSubtypeImpl(T subtype, T supertype);
E extend(String typeParameters);
void run() {
isSubtype('int*', 'num*');
isSubtype('int*', 'Comparable<num*>*');
isSubtype('int*', 'Comparable<Object*>*');
isSubtype('int*', 'Object*');
isSubtype('double*', 'num*');
isNotSubtype('int*', 'double*');
isNotSubtype('int*', 'Comparable<int*>*');
isNotSubtype('int*', 'Iterable<int*>*');
isNotSubtype('Comparable<int*>*', 'Iterable<int*>*');
isSubtype('List<int*>*', 'List<int*>*');
isSubtype('List<int*>*', 'Iterable<int*>*');
isSubtype('List<int*>*', 'List<num*>*');
isSubtype('List<int*>*', 'Iterable<num*>*');
isSubtype('List<int*>*', 'List<Object*>*');
isSubtype('List<int*>*', 'Iterable<Object*>*');
isSubtype('List<int*>*', 'Object*');
isSubtype('List<int*>*', 'List<Comparable<Object*>*>*');
isSubtype('List<int*>*', 'List<Comparable<num*>*>*');
isSubtype('List<int*>*', 'List<Comparable<Comparable<num*>*>*>*');
isNotSubtype('List<int*>*', 'List<double*>*');
isNotSubtype('List<int*>*', 'Iterable<double*>*');
isNotSubtype('List<int*>*', 'Comparable<int*>*');
isNotSubtype('List<int*>*', 'List<Comparable<int*>*>*');
isNotSubtype('List<int*>*', 'List<Comparable<Comparable<int*>*>*>*');
isSubtype('(num*) ->* num*', '(int*) ->* num*');
isSubtype('(num*) ->* int*', '(num*) ->* num*');
isSubtype('(num*) ->* int*', '(int*) ->* num*');
isNotSubtype('(int*) ->* int*', '(num*) ->* num*');
isSubtype('Null?', '(int*) ->* num*');
isSubtype('(num*) ->* (num*) ->* num*', '(num*) ->* (int*) ->* num*');
isNotSubtype('(num*) ->* (int*) ->* int*', '(num*) ->* (num*) ->* num*');
isSubtype('({num* x}) ->* num*', '({int* x}) ->* num*'); // named parameters
isSubtype('(num*, {num* x}) ->* num*', '(int*, {int* x}) ->* num*');
isSubtype('({num* x}) ->* int*', '({num* x}) ->* num*');
isNotSubtype('({int* x}) ->* int*', '({num* x}) ->* num*');
isSubtype('<E>(E) ->* int*', '<E>(E) ->* num*'); // type parameters
isSubtype('<E>(num*) ->* E', '<E>(int*) ->* E');
isSubtype('<E>(E,num*) ->* E', '<E>(E,int*) ->* E');
isNotSubtype('<E>(E,num*) ->* E', '<E>(E,E) ->* E');
isSubtype('<E>(E) ->* (E) ->* E', '<F>(F) ->* (F) ->* F');
isSubtype('<E>(E, (int*,E) ->* E) ->* E', '<E>(E, (int*,E) ->* E) ->* E');
isSubtype('<E>(E, (int*,E) ->* E) ->* E', '<E>(E, (num*,E) ->* E) ->* E');
isNotSubtype('<E,F>(E) ->* (F) ->* E', '<E>(E) ->* <F>(F) ->* E');
isNotSubtype('<E,F>(E) ->* (F) ->* E', '<F,E>(E) ->* (F) ->* E');
isNotSubtype('<E>(E,num*) ->* E', '<E extends num*>(E*,E*) ->* E*');
isNotSubtype(
'<E extends num*>(E*) ->* int*', '<E extends int*>(E*) ->* int*');
isNotSubtype('<E extends num*>(E*) ->* E*', '<E extends int*>(E*) ->* E*');
isNotSubtype(
'<E extends num*>(int*) ->* E*', '<E extends int*>(int*) ->* E*');
isSubtype('<E extends num*>(E*) ->* E*', '<F extends num*>(F*) ->* num*');
isSubtype('<E extends int*>(E*) ->* E*', '<F extends int*>(F*) ->* num*');
isSubtype('<E extends int*>(E*) ->* E*', '<F extends int*>(F*) ->* int*');
isNotSubtype('<E>(int*) ->* int*', '(int*) ->* int*');
isNotSubtype('<E,F>(int*) ->* int*', '<E>(int*) ->* int*');
isSubtype(
'<E extends List<E*>*>(E*) ->* E*', '<F extends List<F*>*>(F*) ->* F*');
isNotSubtype('<E extends Iterable<E*>*>(E*) ->* E*',
'<F extends List<F*>*>(F*) ->* F*');
isNotSubtype(
'<E>(E,List<Object*>*) ->* E*', '<F extends List<F*>*>(F*,F*) ->* F*');
isNotSubtype('<E>(E,List<Object*>*) ->* List<E>*',
'<F extends List<F*>*>(F*,F*) ->* F*');
isNotSubtype('<E>(E,List<Object*>*) ->* int*',
'<F extends List<F*>*>(F*,F*) ->* F*');
isNotSubtype(
'<E>(E,List<Object*>*) ->* E', '<F extends List<F*>*>(F*,F*) ->* void');
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*');
isSubtype('Null?', 'FutureOr<int*>*');
isSubtype('Null?', 'Future<int*>*');
isSubtype('dynamic', 'FutureOr<dynamic>*');
isNotSubtype('dynamic', 'FutureOr<String*>*');
isSubtype('void', 'FutureOr<void>*');
isNotSubtype('void', 'FutureOr<String*>*');
isSubtype('E', 'FutureOr<E>*', typeParameters: 'E');
isNotSubtype('E', 'FutureOr<String*>*', typeParameters: 'E');
isSubtype('() ->* String*', 'FutureOr<() ->* void>*');
isNotSubtype('() ->* void', 'FutureOr<() ->* String>*');
isSubtype('FutureOr<int*>*', 'FutureOr<num*>*');
isNotSubtype('FutureOr<num*>*', 'FutureOr<int*>*');
isSubtype('T & int*', 'FutureOr<num*>*', typeParameters: 'T');
isSubtype('T & Future<num*>*', 'FutureOr<num*>*', typeParameters: 'T');
isSubtype('T & Future<int*>*', 'FutureOr<num*>*', typeParameters: 'T');
if (!skipFutureOrPromotion) {
isSubtype('T & FutureOr<int*>*', 'FutureOr<num*>*', typeParameters: 'T');
isSubtype('T & FutureOr<num*>*', 'FutureOr<num*>*', typeParameters: 'T');
isSubtype('T* & String*', 'FutureOr<num*>*',
typeParameters: 'T extends int*');
isSubtype('T* & Future<String*>*', 'FutureOr<num*>*',
typeParameters: 'T extends Future<num*>*');
isSubtype('T* & FutureOr<String*>*', 'FutureOr<num*>*',
typeParameters: 'T extends FutureOr<int*>*');
isSubtype('T* & FutureOr<String*>*', 'FutureOr<num*>*',
typeParameters: 'T extends FutureOr<num*>*');
}
isNotSubtype('T & num*', 'FutureOr<int*>*', typeParameters: 'T');
isNotSubtype('T & Future<num*>*', 'FutureOr<int*>*', typeParameters: 'T');
isNotSubtype('T & FutureOr<num*>*', 'FutureOr<int*>*', typeParameters: 'T');
isNotSubtype('T* & String*', 'FutureOr<int*>*',
typeParameters: 'T extends num*');
isNotSubtype('T* & Future<String*>*', 'FutureOr<int*>*',
typeParameters: 'T extends Future<num*>*');
isNotSubtype('T* & FutureOr<String*>*', 'FutureOr<int*>*',
typeParameters: 'T extends FutureOr<num*>*');
isSubtype('Id<int*>*', 'FutureOr<num*>*');
isNotSubtype('Id<num*>*', 'FutureOr<int*>*');
isSubtype('FutureOr<Object*>*', 'FutureOr<FutureOr<Object*>*>*');
// T & B <: T & A if B <: A
isSubtype('T & int*', 'T & int*', typeParameters: 'T');
isSubtype('T & int*', 'T & num*', typeParameters: 'T');
isSubtype('T & num*', 'T & num*', typeParameters: 'T');
isNotSubtype('T & num*', 'T & int*', typeParameters: 'T');
isSubtype('Null?', 'T & num*', 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*', typeParameters: 'T extends int*');
isSubtype('T* & int*', 'T*', typeParameters: 'T extends num*');
isSubtype('T* & num*', 'T*', typeParameters: 'T extends num*');
// T extends B <: T & A if B <: A
isSubtype('T*', 'T* & int*', typeParameters: 'T extends int*');
isSubtype('T*', 'T* & num*', typeParameters: 'T extends int*');
isSubtype('T*', 'T* & num*', typeParameters: 'T extends num*');
isNotSubtype('T*', 'T* & int*', typeParameters: 'T extends num*');
// T extends A <: T extends A
isSubtype('T*', 'T*', typeParameters: 'T extends num*');
isSubtype('T', 'T', typeParameters: 'T');
isNotSubtype('S', 'T', typeParameters: 'S, T');
isSubtype('T*', 'T*', typeParameters: 'T extends Object*');
isNotSubtype('S*', 'T*',
typeParameters: 'S extends Object*, T extends Object*');
isSubtype('T', 'T', typeParameters: 'T extends dynamic');
isNotSubtype('S', 'T',
typeParameters: 'S extends dynamic, T extends dynamic');
// S <: T extends S
isNotSubtype('S', 'T', typeParameters: 'S, T extends S');
isSubtype('T', 'S', typeParameters: 'S, T extends S');
// S & B <: A if B <: A, A is not S (or a promotion thereof)
isSubtype('S & int*', 'int*', typeParameters: 'S');
isSubtype('S & int*', 'num*', typeParameters: 'S');
isSubtype('S & num*', 'num*', typeParameters: 'S');
isNotSubtype('S & num*', 'int*', typeParameters: 'S');
isNotSubtype('S & num*', 'T', typeParameters: 'S, T');
isNotSubtype('S & num*', 'T & num*', typeParameters: 'S, T');
// S extends B <: A if B <: A, A is not S (or a promotion thereof)
isSubtype('S*', 'int*', typeParameters: 'S extends int*');
isSubtype('S*', 'num*', typeParameters: 'S extends int*');
isSubtype('S*', 'num*', typeParameters: 'S extends num*');
isNotSubtype('S*', 'int*', typeParameters: 'S extends num*');
isNotSubtype('S*', 'T', typeParameters: 'S extends num*, T');
isNotSubtype('S*', 'T & num*', typeParameters: 'S extends num*, T');
isNotSubtype('dynamic', 'int*');
isNotSubtype('void', 'int*');
isNotSubtype('() ->* int*', 'int*');
isNotSubtype('Typedef<Object*>*', 'int*');
isSubtype('() ->* int*', 'Function*');
isSubtype('() ->* int*', 'Object*');
isNotSubtype('Null?', 'bottom');
isSubtype('Null?', 'Object*');
isSubtype('Null?', 'void');
isSubtype('Null?', 'dynamic');
isSubtype('Null?', 'double*');
isSubtype('Null?', 'Comparable<Object*>*');
isSubtype('Null?', 'Typedef<Object*>*');
isSubtype('Null?', 'T', typeParameters: 'T extends Object*');
isSubtype('Null?', 'Null?');
isSubtype('bottom', 'bottom');
isSubtype('Object*', 'Object*');
isSubtype('Object*', 'dynamic');
isSubtype('Object*', 'void');
isSubtype('dynamic', 'Object*');
isSubtype('dynamic', 'dynamic');
isSubtype('dynamic', 'void');
isSubtype('void', 'Object*');
isSubtype('void', 'dynamic');
isSubtype('void', 'void');
// Check that the top types are equivalent.
isSubtype('<S extends Object*, T extends void>(S, T) ->* void',
'<U extends dynamic, V extends Object*>(U, V) ->* void');
{
String f = '<T extends dynamic>() ->* T';
String g = '<T extends Object*>() ->* T*';
isSubtype(f, g);
isSubtype(g, f);
}
{
String h = '<T extends List<dynamic>*>() ->* T*';
String i = '<T extends List<Object*>*>() ->* T*';
String j = '<T extends List<void>*>() ->* T*';
isSubtype(h, i);
isSubtype(h, j);
isSubtype(i, h);
isSubtype(i, j);
isSubtype(j, h);
isSubtype(j, i);
}
isNotSubtype('dynamic', '() ->* dynamic');
isNotSubtype('FutureOr<() ->* void>*', '() ->* void');
isSubtype('T & () ->* void', '() ->* void', typeParameters: 'T');
isSubtype('T & () ->* void', '() ->* dynamic', typeParameters: 'T');
isSubtype('T & () ->* void', '() ->* Object*', typeParameters: 'T');
isSubtype('T & (void) ->* void', '(void) ->* void', typeParameters: 'T');
isSubtype('T & (void) ->* void', '(dynamic) ->* dynamic',
typeParameters: 'T');
isSubtype('T & (void) ->* void', '(Object*) ->* Object*',
typeParameters: 'T');
isSubtype('T & (void) ->* void', '(void) ->* void', typeParameters: 'T');
isSubtype('T & (void) ->* void', '(Iterable<int*>*) ->* dynamic',
typeParameters: 'T');
isSubtype('T & (void) ->* void', '(int*) ->* Object*', typeParameters: 'T');
isNotSubtype('T & (void) ->* void', '(int*) ->* int*', typeParameters: 'T');
isSubtype('T*', '() ->* void', typeParameters: 'T extends () ->* void');
isNotSubtype('T', '() ->* void', typeParameters: 'T');
isNotSubtype('Typedef<void>*', '() ->* void');
isSubtype('VoidFunction*', '() ->* void');
isNotSubtype(
'DefaultTypes<void, void, List<void>*, List<void>*, '
'int*, (int*) ->* void, () ->* int>*',
'() ->* void');
isNotSubtype('void', '() ->* void');
isNotSubtype('dynamic', 'T', typeParameters: 'T');
isNotSubtype('Iterable<T>*', 'T', typeParameters: 'T');
isNotSubtype('() ->* void', 'T', typeParameters: 'T');
isNotSubtype('FutureOr<T>*', 'T', typeParameters: 'T');
isSubtype('Id<T>*', 'T', typeParameters: 'T');
isNotSubtype('VoidFunction*', 'T*',
typeParameters: 'T extends () ->* void');
isNotSubtype('void', 'T', typeParameters: 'T extends void');
isSubtype('dynamic', 'Id<dynamic>*');
isNotSubtype('dynamic', 'Id<int*>*');
isSubtype('() ->* void', 'Id<() ->* void>*');
isNotSubtype('() ->* void', 'Id<() ->* int>*');
isNotSubtype('FutureOr<() ->* void>*', 'Id<() ->* void>*');
isSubtype('FutureOr<() ->* void>*', 'Id<FutureOr<() ->* void>*>*');
isSubtype('int*', 'Id<int*>*');
isSubtype('T & () ->* void', 'Id<() ->* void>*', typeParameters: 'T');
isSubtype('T & () ->* void', 'Id<() ->* dynamic>*', typeParameters: 'T');
isSubtype('T & () ->* void', 'Id<() ->* Object*>*', typeParameters: 'T');
isSubtype('T & (void) ->* void', 'Id<(void) ->* void>*',
typeParameters: 'T');
isSubtype('T & (void) ->* void', 'Id<(dynamic) ->* dynamic>*',
typeParameters: 'T');
isSubtype('T & (void) ->* void', 'Id<(Object*) ->* Object*>*',
typeParameters: 'T');
isSubtype('T & (void) ->* void', 'Id<(void) ->* void>*',
typeParameters: 'T');
isSubtype('T & (void) ->* void', 'Id<(Iterable<int*>*) ->* dynamic>*',
typeParameters: 'T');
isSubtype('T & (void) ->* void', 'Id<(int*) ->* Object*>*',
typeParameters: 'T');
isNotSubtype('T & (void) ->* void', 'Id<(int*) ->* int*>*',
typeParameters: 'T');
isNotSubtype('dynamic', 'T & dynamic', typeParameters: 'T extends dynamic');
isNotSubtype('() ->* T', 'T & () ->* T', typeParameters: 'T');
isNotSubtype('FutureOr<T & String*>*', 'T & String*', typeParameters: 'T');
isSubtype('Id<T & String*>*', 'T & String*', typeParameters: 'T');
isSubtype('Id<T & String*>*', 'T', typeParameters: 'T');
isSubtype('Id<T & String*>*', 'String*', typeParameters: 'T');
isNotSubtype('Id<T & String*>*', 'S & String*', typeParameters: 'T, S');
isNotSubtype('void', 'T & void', typeParameters: 'T');
isNotSubtype('void', 'T & void', typeParameters: 'T extends void');
isSubtype('T', 'Id<T>*', typeParameters: 'T');
isSubtype('T', 'Id<Object*>*', typeParameters: 'T');
isNotSubtype('T', 'Id<Comparable<int*>*>*', typeParameters: 'T');
isSubtype('T*', 'Id<Comparable<int*>*>*',
typeParameters: 'T extends Comparable<int*>*');
isSubtype('Id<int*>*', 'Id<int*>*');
isSubtype('Id<int*>*', 'Id<Object*>*');
isNotSubtype('Id<Object*>*', 'Id<int*>*');
isSubtype('Id<() ->* int*>*', 'Id<() ->* int*>*');
isSubtype('Id<() ->* int*>*', 'Id<() ->* Object*>*');
isNotSubtype('Id<() ->* Object*>*', 'Id<() ->* int*>*');
isSubtype('void', 'Id<void>*');
isNotSubtype('void', 'Id<Null?>*');
// The following function type tests are derived from
// ../../../../../tests/compiler/dart2js/model/subtype_test.dart.
isSubtype("() ->* int*", 'Function*');
isNotSubtype('Function*', "() ->* int*");
isSubtype("() ->* dynamic", "() ->* dynamic");
isSubtype("() ->* dynamic", "() ->* void");
isSubtype("() ->* void", "() ->* dynamic");
isSubtype("() ->* int*", "() ->* void");
isNotSubtype("() ->* void", "() ->* int*");
isSubtype("() ->* void", "() ->* void");
isSubtype("() ->* int*", "() ->* int*");
isSubtype("() ->* int*", "() ->* Object*");
isNotSubtype("() ->* int*", "() ->* double*");
isNotSubtype("() ->* int*", "(int*) ->* void");
isNotSubtype("() ->* void", "(int*) ->* int*");
isNotSubtype("() ->* void", "(int*) ->* void");
isSubtype("(int*) ->* int*", "(int*) ->* int*");
isSubtype("(Object*) ->* int*", "(int*) ->* Object*");
isNotSubtype("(int*) ->* int*", "(double*) ->* int*");
isNotSubtype("() ->* int*", "(int*) ->* int*");
isNotSubtype("(int*) ->* int*", "(int*, int*) ->* int*");
isNotSubtype("(int*, int*) ->* int*", "(int*) ->* int*");
isNotSubtype("(() ->* void) ->* void", "((int*) ->* void) ->* void");
isNotSubtype("((int*) ->* void) ->* void", "(() ->* void) ->* void");
// Optional positional parameters.
isSubtype("([int*]) ->* void", "() ->* void");
isSubtype("([int*]) ->* void", "(int*) ->* void");
isNotSubtype("(int*) ->* void", "([int*]) ->* void");
isSubtype("([int*]) ->* void", "([int*]) ->* void");
isSubtype("([Object*]) ->* void", "([int*]) ->* void");
isNotSubtype("([int*]) ->* void", "([Object*]) ->* void");
isSubtype("(int*, [int*]) ->* void", "(int*) ->* void");
isSubtype("(int*, [int*]) ->* void", "(int*, [int*]) ->* void");
isNotSubtype("(int*) ->* void", "([int*]) ->* void");
isSubtype("([int*, int*]) ->* void", "(int*) ->* void");
isSubtype("([int*, int*]) ->* void", "(int*, [int*]) ->* void");
isNotSubtype("([int*, int*]) ->* void", "(int*, [int*, int*]) ->* void");
isSubtype("([int*, int*, int*]) ->* void", "(int*, [int*, int*]) ->* void");
isNotSubtype("([int*]) ->* void", "(double*) ->* void");
isNotSubtype("([int*]) ->* void", "([int*, int*]) ->* void");
isSubtype("([int*, int*]) ->* void", "([int*]) ->* void");
isSubtype("([Object*, int*]) ->* void", "([int*]) ->* void");
// Optional named parameters.
isSubtype("({int* a}) ->* void", "() ->* void");
isNotSubtype("({int* a}) ->* void", "(int*) ->* void");
isNotSubtype("(int*) ->* void", "({int* a}) ->* void");
isSubtype("({int* a}) ->* void", "({int* a}) ->* void");
isNotSubtype("({int* a}) ->* void", "({int* b}) ->* void");
isSubtype("({Object* a}) ->* void", "({int* a}) ->* void");
isNotSubtype("({int* a}) ->* void", "({Object* a}) ->* void");
isSubtype("(int*, {int* a}) ->* void", "(int*, {int* a}) ->* void");
isNotSubtype("({int* a}) ->* void", "({double* a}) ->* void");
isNotSubtype("({int* a}) ->* void", "({int* a, int* b}) ->* void");
isSubtype("({int* a, int* b}) ->* void", "({int* a}) ->* void");
isSubtype(
"({int* a, int* b, int* c}) ->* void", "({int* a, int* c}) ->* void");
isSubtype(
"({int* c, int* b, int* a}) ->* void", "({int* a, int* c}) ->* void");
isSubtype(
"({int* a, int* b, int* c}) ->* void", "({int* b, int* c}) ->* void");
isSubtype(
"({int* c, int* b, int* a}) ->* void", "({int* b, int* c}) ->* void");
isSubtype("({int* a, int* b, int* c}) ->* void", "({int* c}) ->* void");
isSubtype("({int* c, int* b, int* a}) ->* void", "({int* c}) ->* void");
// Parsing of nullable and legacy types.
isSubtype("int?", "int?");
isSubtype("int*", "int*");
isSubtype("(int) ->? int", "(int) ->? int");
isSubtype("(int) ->* int", "(int) ->* int");
isSubtype("(int, int*, int?) -> int?", "(int, int*, int?) -> int?");
isSubtype("List<int>?", "List<int>?");
isSubtype("List<int?>?", "List<int?>?");
isSubtype("T & int?", "T & int?", typeParameters: "T extends Object?");
isSubtype("T? & int?", "T? & int?", typeParameters: "T extends Object");
}
}