blob: 3d880548556b71d7e8da95498baefaa726700849 [file] [log] [blame]
// Copyright (c) 2013, 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.
// @dart = 2.7
library subtype_test;
import 'dart:async';
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/elements/entities.dart' show ClassEntity;
import 'package:compiler/src/elements/types.dart';
import 'package:expect/expect.dart';
import '../helpers/type_test_helper.dart';
void main() {
asyncTest(() async {
await runTests();
});
}
Future runTests() async {
await testCallableSubtype();
await testInterfaceSubtype();
await testFunctionSubtyping();
await testTypedefSubtyping();
await testFunctionSubtypingOptional();
await testTypedefSubtypingOptional();
await testFunctionSubtypingNamed();
await testTypedefSubtypingNamed();
await testTypeVariableSubtype();
await testStrongModeSubtyping();
}
void testTypes(TypeEnvironment env, DartType subtype, DartType supertype,
bool expectSubtype) {
Expect.equals(expectSubtype, env.isSubtype(subtype, supertype),
'$subtype <: $supertype');
if (expectSubtype) {
Expect.isTrue(env.isPotentialSubtype(subtype, supertype),
'$subtype <: $supertype (potential)');
}
}
void testElementTypes(
TypeEnvironment env, String subname, String supername, bool expectSubtype) {
DartType subtype = env.getElementType(subname);
DartType supertype = env.getElementType(supername);
testTypes(env, subtype, supertype, expectSubtype);
}
Future testInterfaceSubtype() async {
await TypeEnvironment.create(r"""
class A<T> {}
class B<T1, T2> extends A<T1> {}
// TODO(johnniwinther): Inheritance with different type arguments is
// currently not supported by the implementation.
class C<T1, T2> extends B<T2, T1> /*implements A<A<T1>>*/ {}
main() {
new C();
}
""", options: [Flags.noSoundNullSafety], expectNoErrors: true)
.then((env) {
void expect(bool expectSubtype, DartType T, DartType S) {
testTypes(env, T, S, expectSubtype);
}
var types = env.types;
ClassEntity A = env.getClass('A');
ClassEntity B = env.getClass('B');
ClassEntity C = env.getClass('C');
DartType Object_ = env['Object'];
DartType num_ = env['num'];
DartType int_ = env['int'];
DartType String_ = env['String'];
DartType dynamic_ = env['dynamic'];
DartType void_ = env['void'];
DartType Null_ = env['Null'];
expect(true, void_, void_);
expect(true, void_, dynamic_);
// Unsure about the next one, see dartbug.com/14933.
expect(true, dynamic_, void_);
expect(true, void_, Object_);
expect(true, Object_, void_);
expect(true, Null_, void_);
expect(true, Object_, Object_);
expect(true, num_, Object_);
expect(true, int_, Object_);
expect(true, String_, Object_);
expect(true, dynamic_, Object_);
expect(true, Null_, Object_);
expect(false, Object_, num_);
expect(true, num_, num_);
expect(true, int_, num_);
expect(false, String_, num_);
expect(false, dynamic_, num_);
expect(true, Null_, num_);
expect(false, Object_, int_);
expect(false, num_, int_);
expect(true, int_, int_);
expect(false, String_, int_);
expect(false, dynamic_, int_);
expect(true, Null_, int_);
expect(false, Object_, String_);
expect(false, num_, String_);
expect(false, int_, String_);
expect(true, String_, String_);
expect(false, dynamic_, String_);
expect(true, Null_, String_);
expect(true, Object_, dynamic_);
expect(true, num_, dynamic_);
expect(true, int_, dynamic_);
expect(true, String_, dynamic_);
expect(true, dynamic_, dynamic_);
expect(true, Null_, dynamic_);
expect(false, Object_, Null_);
expect(false, num_, Null_);
expect(false, int_, Null_);
expect(false, String_, Null_);
expect(false, dynamic_, Null_);
expect(true, Null_, Null_);
DartType A_Object = instantiate(types, A, [Object_]);
DartType A_num = instantiate(types, A, [num_]);
DartType A_int = instantiate(types, A, [int_]);
DartType A_String = instantiate(types, A, [String_]);
DartType A_dynamic = instantiate(types, A, [dynamic_]);
DartType A_Null = instantiate(types, A, [Null_]);
expect(true, A_Object, Object_);
expect(false, A_Object, num_);
expect(false, A_Object, int_);
expect(false, A_Object, String_);
expect(true, A_Object, dynamic_);
expect(false, A_Object, Null_);
expect(true, A_Object, A_Object);
expect(true, A_num, A_Object);
expect(true, A_int, A_Object);
expect(true, A_String, A_Object);
expect(true, A_dynamic, A_Object);
expect(true, A_Null, A_Object);
expect(false, A_Object, A_num);
expect(true, A_num, A_num);
expect(true, A_int, A_num);
expect(false, A_String, A_num);
expect(false, A_dynamic, A_num);
expect(true, A_Null, A_num);
expect(false, A_Object, A_int);
expect(false, A_num, A_int);
expect(true, A_int, A_int);
expect(false, A_String, A_int);
expect(false, A_dynamic, A_int);
expect(true, A_Null, A_int);
expect(false, A_Object, A_String);
expect(false, A_num, A_String);
expect(false, A_int, A_String);
expect(true, A_String, A_String);
expect(false, A_dynamic, A_String);
expect(true, A_Null, A_String);
expect(true, A_Object, A_dynamic);
expect(true, A_num, A_dynamic);
expect(true, A_int, A_dynamic);
expect(true, A_String, A_dynamic);
expect(true, A_dynamic, A_dynamic);
expect(true, A_Null, A_dynamic);
expect(false, A_Object, A_Null);
expect(false, A_num, A_Null);
expect(false, A_int, A_Null);
expect(false, A_String, A_Null);
expect(false, A_dynamic, A_Null);
expect(true, A_Null, A_Null);
DartType B_Object_Object = instantiate(types, B, [Object_, Object_]);
DartType B_num_num = instantiate(types, B, [num_, num_]);
DartType B_int_num = instantiate(types, B, [int_, num_]);
DartType B_dynamic_dynamic = instantiate(types, B, [dynamic_, dynamic_]);
DartType B_String_dynamic = instantiate(types, B, [String_, dynamic_]);
expect(true, B_Object_Object, Object_);
expect(true, B_Object_Object, A_Object);
expect(false, B_Object_Object, A_num);
expect(false, B_Object_Object, A_int);
expect(false, B_Object_Object, A_String);
expect(true, B_Object_Object, A_dynamic);
expect(true, B_num_num, Object_);
expect(true, B_num_num, A_Object);
expect(true, B_num_num, A_num);
expect(false, B_num_num, A_int);
expect(false, B_num_num, A_String);
expect(true, B_num_num, A_dynamic);
expect(true, B_int_num, Object_);
expect(true, B_int_num, A_Object);
expect(true, B_int_num, A_num);
expect(true, B_int_num, A_int);
expect(false, B_int_num, A_String);
expect(true, B_int_num, A_dynamic);
expect(true, B_dynamic_dynamic, Object_);
expect(true, B_dynamic_dynamic, A_Object);
expect(false, B_dynamic_dynamic, A_num);
expect(false, B_dynamic_dynamic, A_int);
expect(false, B_dynamic_dynamic, A_String);
expect(true, B_dynamic_dynamic, A_dynamic);
expect(true, B_String_dynamic, Object_);
expect(true, B_String_dynamic, A_Object);
expect(false, B_String_dynamic, A_num);
expect(false, B_String_dynamic, A_int);
expect(true, B_String_dynamic, A_String);
expect(true, B_String_dynamic, A_dynamic);
expect(true, B_Object_Object, B_Object_Object);
expect(true, B_num_num, B_Object_Object);
expect(true, B_int_num, B_Object_Object);
expect(true, B_dynamic_dynamic, B_Object_Object);
expect(true, B_String_dynamic, B_Object_Object);
expect(false, B_Object_Object, B_num_num);
expect(true, B_num_num, B_num_num);
expect(true, B_int_num, B_num_num);
expect(false, B_dynamic_dynamic, B_num_num);
expect(false, B_String_dynamic, B_num_num);
expect(false, B_Object_Object, B_int_num);
expect(false, B_num_num, B_int_num);
expect(true, B_int_num, B_int_num);
expect(false, B_dynamic_dynamic, B_int_num);
expect(false, B_String_dynamic, B_int_num);
expect(true, B_Object_Object, B_dynamic_dynamic);
expect(true, B_num_num, B_dynamic_dynamic);
expect(true, B_int_num, B_dynamic_dynamic);
expect(true, B_dynamic_dynamic, B_dynamic_dynamic);
expect(true, B_String_dynamic, B_dynamic_dynamic);
expect(false, B_Object_Object, B_String_dynamic);
expect(false, B_num_num, B_String_dynamic);
expect(false, B_int_num, B_String_dynamic);
expect(false, B_dynamic_dynamic, B_String_dynamic);
expect(true, B_String_dynamic, B_String_dynamic);
DartType C_Object_Object = instantiate(types, C, [Object_, Object_]);
DartType C_num_num = instantiate(types, C, [num_, num_]);
DartType C_int_String = instantiate(types, C, [int_, String_]);
DartType C_dynamic_dynamic = instantiate(types, C, [dynamic_, dynamic_]);
expect(true, C_Object_Object, B_Object_Object);
expect(false, C_Object_Object, B_num_num);
expect(false, C_Object_Object, B_int_num);
expect(true, C_Object_Object, B_dynamic_dynamic);
expect(false, C_Object_Object, B_String_dynamic);
expect(true, C_num_num, B_Object_Object);
expect(true, C_num_num, B_num_num);
expect(false, C_num_num, B_int_num);
expect(true, C_num_num, B_dynamic_dynamic);
expect(false, C_num_num, B_String_dynamic);
expect(true, C_int_String, B_Object_Object);
expect(false, C_int_String, B_num_num);
expect(false, C_int_String, B_int_num);
expect(true, C_int_String, B_dynamic_dynamic);
expect(true, C_int_String, B_String_dynamic);
expect(true, C_dynamic_dynamic, B_Object_Object);
expect(false, C_dynamic_dynamic, B_num_num);
expect(false, C_dynamic_dynamic, B_int_num);
expect(true, C_dynamic_dynamic, B_dynamic_dynamic);
expect(false, C_dynamic_dynamic, B_String_dynamic);
expect(false, C_int_String, A_int);
expect(true, C_int_String, A_String);
// TODO(johnniwinther): Inheritance with different type arguments is
// currently not supported by the implementation.
//expect(true, C_int_String, instantiate(A, [A_int]));
expect(false, C_int_String, instantiate(types, A, [A_String]));
});
}
Future testCallableSubtype() async {
await TypeEnvironment.create(r"""
class U {}
class V extends U {}
class W extends V {}
class A {
int call(V v, int i) => null;
int m1(U u, int i) => null;
int m2(W w, num n) => null;
U m3(V v, int i) => null;
int m4(V v, U u) => null;
void m5(V v, int i) => null;
}
main() {
new W();
var a = new A();
a.call(null, null);
a.m1(null, null);
a.m2(null, null);
a.m3(null, null);
a.m4(null, null);
a.m5(null, null);
}
""", options: [Flags.noSoundNullSafety], expectNoErrors: true)
.then((env) {
void expect(bool expectSubtype, DartType T, DartType S) {
testTypes(env, T, S, expectSubtype);
}
ClassEntity classA = env.getClass('A');
DartType A = env.elementEnvironment.getRawType(classA);
DartType function = env['Function'];
DartType call = env.getMemberType('call', classA);
DartType m1 = env.getMemberType('m1', classA);
DartType m2 = env.getMemberType('m2', classA);
DartType m3 = env.getMemberType('m3', classA);
DartType m4 = env.getMemberType('m4', classA);
DartType m5 = env.getMemberType('m5', classA);
expect(false, A, function);
expect(false, A, call);
expect(false, call, m1);
expect(false, A, m1);
expect(false, A, m2);
expect(false, A, m3);
expect(false, A, m4);
expect(false, A, m5);
});
}
const List<FunctionTypeData> functionTypesData = const <FunctionTypeData>[
const FunctionTypeData('', '_', '()'),
const FunctionTypeData('void', 'void_', '()'),
const FunctionTypeData('void', 'void_2', '()'),
const FunctionTypeData('int', 'int_', '()'),
const FunctionTypeData('int', 'int_2', '()'),
const FunctionTypeData('Object', 'Object_', '()'),
const FunctionTypeData('double', 'double_', '()'),
const FunctionTypeData('void', 'void__int', '(int i)'),
const FunctionTypeData('int', 'int__int', '(int i)'),
const FunctionTypeData('int', 'int__int2', '(int i)'),
const FunctionTypeData('int', 'int__Object', '(Object o)'),
const FunctionTypeData('Object', 'Object__int', '(int i)'),
const FunctionTypeData('int', 'int__double', '(double d)'),
const FunctionTypeData('int', 'int__int_int', '(int i1, int i2)'),
const FunctionTypeData('void', 'inline_void_', '(void Function() f)'),
const FunctionTypeData(
'void', 'inline_void__int', '(void Function(int i) f)'),
];
Future testFunctionSubtyping() async {
await TypeEnvironment.create(
createMethods(functionTypesData, additionalData: """
main() {
${createUses(functionTypesData)}
}
"""),
options: [Flags.noSoundNullSafety],
expectNoErrors: true)
.then(functionSubtypingHelper);
}
Future testTypedefSubtyping() async {
await TypeEnvironment.create(
createTypedefs(functionTypesData, additionalData: """
main() {
${createUses(functionTypesData)}
}
"""),
options: [Flags.noSoundNullSafety],
expectNoErrors: true)
.then(functionSubtypingHelper);
}
functionSubtypingHelper(TypeEnvironment env) {
void expect(bool expectSubtype, String sub, String sup) {
testElementTypes(env, sub, sup, expectSubtype);
}
// () -> int <: Function
expect(true, 'int_', 'Function');
// Function <: () -> int
expect(false, 'Function', 'int_');
// () -> dynamic <: () -> dynamic
expect(true, '_', '_');
// () -> dynamic <: () -> void
expect(true, '_', 'void_');
// () -> void <: () -> dynamic
expect(true, 'void_', '_');
// () -> int <: () -> void
expect(true, 'int_', 'void_');
// () -> void <: () -> int
expect(false, 'void_', 'int_');
// () -> void <: () -> void
expect(true, 'void_', 'void_2');
// () -> int <: () -> int
expect(true, 'int_', 'int_2');
// () -> int <: () -> Object
expect(true, 'int_', 'Object_');
// () -> int <: () -> double
expect(false, 'int_', 'double_');
// () -> int <: (int) -> void
expect(false, 'int_', 'void__int');
// () -> void <: (int) -> int
expect(false, 'void_', 'int__int');
// () -> void <: (int) -> void
expect(false, 'void_', 'void__int');
// (int) -> int <: (int) -> int
expect(true, 'int__int', 'int__int2');
// (Object) -> int <: (int) -> Object
expect(true, 'int__Object', 'Object__int');
// (int) -> int <: (double) -> int
expect(false, 'int__int', 'int__double');
// () -> int <: (int) -> int
expect(false, 'int_', 'int__int');
// (int) -> int <: (int,int) -> int
expect(false, 'int__int', 'int__int_int');
// (int,int) -> int <: (int) -> int
expect(false, 'int__int_int', 'int__int');
// (()->void) -> void <: ((int)->void) -> void
expect(false, 'inline_void_', 'inline_void__int');
// ((int)->void) -> void <: (()->void) -> void
expect(false, 'inline_void__int', 'inline_void_');
}
const List<FunctionTypeData> optionalFunctionTypesData =
const <FunctionTypeData>[
const FunctionTypeData('void', 'void_', '()'),
const FunctionTypeData('void', 'void__int', '(int i)'),
const FunctionTypeData('void', 'void___int', '([int i])'),
const FunctionTypeData('void', 'void___int2', '([int i])'),
const FunctionTypeData('void', 'void___Object', '([Object o])'),
const FunctionTypeData('void', 'void__int__int', '(int i1, [int i2])'),
const FunctionTypeData('void', 'void__int__int2', '(int i1, [int i2])'),
const FunctionTypeData(
'void', 'void__int__int_int', '(int i1, [int i2, int i3])'),
const FunctionTypeData('void', 'void___double', '(double d)'),
const FunctionTypeData('void', 'void___int_int', '([int i1, int i2])'),
const FunctionTypeData(
'void', 'void___int_int_int', '([int i1, int i2, int i3])'),
const FunctionTypeData('void', 'void___Object_int', '([Object o, int i])'),
];
Future testFunctionSubtypingOptional() async {
await TypeEnvironment.create(
createMethods(optionalFunctionTypesData, additionalData: """
main() {
${createUses(optionalFunctionTypesData)}
}
"""),
options: [Flags.noSoundNullSafety],
expectNoErrors: true)
.then((env) => functionSubtypingOptionalHelper(env));
}
Future testTypedefSubtypingOptional() async {
await TypeEnvironment.create(
createTypedefs(optionalFunctionTypesData, additionalData: """
main() {
${createUses(optionalFunctionTypesData)}
}
"""),
options: [Flags.noSoundNullSafety],
expectNoErrors: true)
.then((env) => functionSubtypingOptionalHelper(env));
}
functionSubtypingOptionalHelper(TypeEnvironment env) {
void expect(bool expectSubtype, String sub, String sup) {
testElementTypes(env, sub, sup, expectSubtype);
}
// Test ([int])->void <: ()->void.
expect(true, 'void___int', 'void_');
// Test ([int])->void <: (int)->void.
expect(true, 'void___int', 'void__int');
// Test (int)->void <: ([int])->void.
expect(false, 'void__int', 'void___int');
// Test ([int])->void <: ([int])->void.
expect(true, 'void___int', 'void___int2');
// Test ([Object])->void <: ([int])->void.
expect(true, 'void___Object', 'void___int');
// Test ([int])->void <: ([Object])->void.
expect(false, 'void___int', 'void___Object');
// Test (int,[int])->void <: (int)->void.
expect(true, 'void__int__int', 'void__int');
// Test (int,[int])->void <: (int,[int])->void.
expect(true, 'void__int__int', 'void__int__int2');
// Test (int)->void <: ([int])->void.
expect(false, 'void__int', 'void___int');
// Test ([int,int])->void <: (int)->void.
expect(true, 'void___int_int', 'void__int');
// Test ([int,int])->void <: (int,[int])->void.
expect(true, 'void___int_int', 'void__int__int');
// Test ([int,int])->void <: (int,[int,int])->void.
expect(false, 'void___int_int', 'void__int__int_int');
// Test ([int,int,int])->void <: (int,[int,int])->void.
expect(true, 'void___int_int_int', 'void__int__int_int');
// Test ([int])->void <: ([double])->void.
expect(false, 'void___int', 'void___double');
// Test ([int])->void <: ([int,int])->void.
expect(false, 'void___int', 'void___int_int');
// Test ([int,int])->void <: ([int])->void.
expect(true, 'void___int_int', 'void___int');
// Test ([Object,int])->void <: ([int])->void.
expect(true, 'void___Object_int', 'void___int');
}
const List<FunctionTypeData> namedFunctionTypesData = const <FunctionTypeData>[
const FunctionTypeData('void', 'void_', '()'),
const FunctionTypeData('void', 'void__int', '(int i)'),
const FunctionTypeData('void', 'void___a_int', '({int a})'),
const FunctionTypeData('void', 'void___a_int2', '({int a})'),
const FunctionTypeData('void', 'void___b_int', '({int b})'),
const FunctionTypeData('void', 'void___a_Object', '({Object a})'),
const FunctionTypeData('void', 'void__int__a_int', '(int i1, {int a})'),
const FunctionTypeData('void', 'void__int__a_int2', '(int i1, {int a})'),
const FunctionTypeData('void', 'void___a_double', '({double a})'),
const FunctionTypeData('void', 'void___a_int_b_int', '({int a, int b})'),
const FunctionTypeData(
'void', 'void___a_int_b_int_c_int', '({int a, int b, int c})'),
const FunctionTypeData('void', 'void___a_int_c_int', '({int a, int c})'),
const FunctionTypeData('void', 'void___b_int_c_int', '({int b, int c})'),
const FunctionTypeData('void', 'void___c_int', '({int c})'),
];
Future testFunctionSubtypingNamed() async {
await TypeEnvironment.create(
createMethods(namedFunctionTypesData, additionalData: """
main() {
${createUses(namedFunctionTypesData)}
}
"""),
options: [Flags.noSoundNullSafety],
expectNoErrors: true)
.then((env) => functionSubtypingNamedHelper(env));
}
Future testTypedefSubtypingNamed() async {
await TypeEnvironment.create(
createTypedefs(namedFunctionTypesData, additionalData: """
main() {
${createUses(namedFunctionTypesData)}
}
"""),
options: [Flags.noSoundNullSafety],
expectNoErrors: true)
.then((env) => functionSubtypingNamedHelper(env));
}
functionSubtypingNamedHelper(TypeEnvironment env) {
expect(bool expectSubtype, String sub, String sup) {
testElementTypes(env, sub, sup, expectSubtype);
}
// Test ({int a})->void <: ()->void.
expect(true, 'void___a_int', 'void_');
// Test ({int a})->void <: (int)->void.
expect(false, 'void___a_int', 'void__int');
// Test (int)->void <: ({int a})->void.
expect(false, 'void__int', 'void___a_int');
// Test ({int a})->void <: ({int a})->void.
expect(true, 'void___a_int', 'void___a_int2');
// Test ({int a})->void <: ({int b})->void.
expect(false, 'void___a_int', 'void___b_int');
// Test ({Object a})->void <: ({int a})->void.
expect(true, 'void___a_Object', 'void___a_int');
// Test ({int a})->void <: ({Object a})->void.
expect(false, 'void___a_int', 'void___a_Object');
// Test (int,{int a})->void <: (int,{int a})->void.
expect(true, 'void__int__a_int', 'void__int__a_int2');
// Test ({int a})->void <: ({double a})->void.
expect(false, 'void___a_int', 'void___a_double');
// Test ({int a})->void <: ({int a,int b})->void.
expect(false, 'void___a_int', 'void___a_int_b_int');
// Test ({int a,int b})->void <: ({int a})->void.
expect(true, 'void___a_int_b_int', 'void___a_int');
// Test ({int a,int b,int c})->void <: ({int a,int c})->void.
expect(true, 'void___a_int_b_int_c_int', 'void___a_int_c_int');
// Test ({int a,int b,int c})->void <: ({int b,int c})->void.
expect(true, 'void___a_int_b_int_c_int', 'void___b_int_c_int');
// Test ({int a,int b,int c})->void <: ({int c})->void.
expect(true, 'void___a_int_b_int_c_int', 'void___c_int');
}
Future testTypeVariableSubtype() async {
await TypeEnvironment.create(r"""
class A<T> {}
class B<T extends Object> {}
class C<T extends num> {}
class D<T extends int> {}
class E<T extends S, S extends num> {}
class F<T extends num, S extends T> {}
main() {
new A();
new B();
new C();
new D();
new E<int, num>();
new F();
}
""", options: [Flags.noSoundNullSafety], expectNoErrors: true)
.then((env) {
void expect(bool expectSubtype, DartType T, DartType S) {
testTypes(env, T, S, expectSubtype);
}
TypeVariableType getTypeVariable(ClassEntity cls, int index) {
return env.elementEnvironment.getThisType(cls).typeArguments[index];
}
ClassEntity A = env.getClass('A');
TypeVariableType A_T = getTypeVariable(A, 0);
ClassEntity B = env.getClass('B');
TypeVariableType B_T = getTypeVariable(B, 0);
ClassEntity C = env.getClass('C');
TypeVariableType C_T = getTypeVariable(C, 0);
ClassEntity D = env.getClass('D');
TypeVariableType D_T = getTypeVariable(D, 0);
ClassEntity E = env.getClass('E');
TypeVariableType E_T = getTypeVariable(E, 0);
TypeVariableType E_S = getTypeVariable(E, 1);
ClassEntity F = env.getClass('F');
TypeVariableType F_T = getTypeVariable(F, 0);
TypeVariableType F_S = getTypeVariable(F, 1);
DartType Object_ = env['Object'];
DartType num_ = env['num'];
DartType int_ = env['int'];
DartType String_ = env['String'];
DartType dynamic_ = env['dynamic'];
// class A<T> {}
expect(true, A_T, Object_);
expect(false, A_T, num_);
expect(false, A_T, int_);
expect(false, A_T, String_);
expect(true, A_T, dynamic_);
expect(true, A_T, A_T);
expect(false, A_T, B_T);
// class B<T extends Object> {}
expect(true, B_T, Object_);
expect(false, B_T, num_);
expect(false, B_T, int_);
expect(false, B_T, String_);
expect(true, B_T, dynamic_);
expect(true, B_T, B_T);
expect(false, B_T, A_T);
// class C<T extends num> {}
expect(true, C_T, Object_);
expect(true, C_T, num_);
expect(false, C_T, int_);
expect(false, C_T, String_);
expect(true, C_T, dynamic_);
expect(true, C_T, C_T);
expect(false, C_T, A_T);
// class D<T extends int> {}
expect(true, D_T, Object_);
expect(true, D_T, num_);
expect(true, D_T, int_);
expect(false, D_T, String_);
expect(true, D_T, dynamic_);
expect(true, D_T, D_T);
expect(false, D_T, A_T);
// class E<T extends S, S extends num> {}
expect(true, E_T, Object_);
expect(true, E_T, num_);
expect(false, E_T, int_);
expect(false, E_T, String_);
expect(true, E_T, dynamic_);
expect(true, E_T, E_T);
expect(true, E_T, E_S);
expect(false, E_T, A_T);
expect(true, E_S, Object_);
expect(true, E_S, num_);
expect(false, E_S, int_);
expect(false, E_S, String_);
expect(true, E_S, dynamic_);
expect(false, E_S, E_T);
expect(true, E_S, E_S);
expect(false, E_S, A_T);
// class F<T extends num, S extends T> {}
expect(true, F_T, Object_);
expect(true, F_T, num_);
expect(false, F_T, int_);
expect(false, F_T, String_);
expect(true, F_T, dynamic_);
expect(false, F_T, F_S);
expect(true, F_T, F_T);
expect(false, F_T, A_T);
expect(true, F_S, Object_);
expect(true, F_S, num_);
expect(false, F_S, int_);
expect(false, F_S, String_);
expect(true, F_S, dynamic_);
expect(true, F_S, F_S);
expect(true, F_S, F_T);
expect(false, F_S, A_T);
});
}
Future testStrongModeSubtyping() async {
await TypeEnvironment.create(r"""
class ClassWithCall {
void call() {}
}
num returnNum() => null;
int returnInt() => null;
void returnVoid() => null;
Object returnObject() => null;
takeNum(num o) => null;
takeInt(int o) => null;
takeVoid(void o) => null;
takeObject(Object o) => null;
main() {
new ClassWithCall().call;
returnNum();
returnInt();
returnVoid();
returnObject();
takeNum(null);
takeInt(null);
takeVoid(null);
takeObject(null);
}
""", options: [Flags.noSoundNullSafety], expectNoErrors: true)
.then((env) {
void expect(bool expectSubtype, DartType T, DartType S) {
Expect.equals(expectSubtype, env.isSubtype(T, S), '$T <: $S');
if (expectSubtype) {
Expect.isTrue(env.isPotentialSubtype(T, S), '$T <: $S (potential)');
}
}
InterfaceType ClassWithCall = env['ClassWithCall'];
DartType Object_ = env['Object'];
DartType dynamic_ = env['dynamic'];
DartType void_ = env['void'];
DartType Null_ = env['Null'];
DartType Function_ = env['Function'];
DartType ClassWithCallType =
env.getMemberType('call', ClassWithCall.element);
InterfaceType List_Object = env.commonElements.listType(Object_);
InterfaceType List_dynamic = env.commonElements.listType(dynamic_);
InterfaceType List_void = env.commonElements.listType(void_);
InterfaceType List_Null = env.commonElements.listType(Null_);
InterfaceType List_Function = env.commonElements.listType(Function_);
DartType returnNum = env.getMemberType('returnNum');
DartType returnInt = env.getMemberType('returnInt');
DartType returnVoid = env.getMemberType('returnVoid');
DartType returnObject = env.getMemberType('returnObject');
DartType takeNum = env.getMemberType('takeNum');
DartType takeInt = env.getMemberType('takeInt');
DartType takeVoid = env.getMemberType('takeVoid');
DartType takeObject = env.getMemberType('takeObject');
// Classes with call methods are no longer subtypes of Function.
expect(false, ClassWithCall, Function_);
// Classes with call methods are no longer subtype the function type of the
// call method.
expect(false, ClassWithCall, ClassWithCallType);
// At runtime `Object`, `dynamic` and `void` are the same and are therefore
// subtypes and supertypes of each other.
//
// `dynamic` is no longer a bottom type but `Null` is.
expect(true, Object_, Object_);
expect(true, Object_, void_);
expect(true, Object_, dynamic_);
expect(false, Object_, Null_);
expect(false, Object_, Function_);
expect(true, dynamic_, Object_);
expect(true, dynamic_, void_);
expect(true, dynamic_, dynamic_);
expect(false, dynamic_, Null_);
expect(false, dynamic_, Function_);
expect(true, void_, Object_);
expect(true, void_, void_);
expect(true, void_, dynamic_);
expect(false, void_, Null_);
expect(false, void_, Function_);
expect(true, Null_, Object_);
expect(true, Null_, void_);
expect(true, Null_, dynamic_);
expect(true, Null_, Null_);
expect(true, Null_, Function_);
expect(true, Function_, Object_);
expect(true, Function_, void_);
expect(true, Function_, dynamic_);
expect(false, Function_, Null_);
expect(true, Function_, Function_);
expect(true, List_Object, List_Object);
expect(true, List_Object, List_void);
expect(true, List_Object, List_dynamic);
expect(false, List_Object, List_Null);
expect(false, List_Object, List_Function);
expect(true, List_dynamic, List_Object);
expect(true, List_dynamic, List_void);
expect(true, List_dynamic, List_dynamic);
expect(false, List_dynamic, List_Null);
expect(false, List_dynamic, List_Function);
expect(true, List_void, List_Object);
expect(true, List_void, List_void);
expect(true, List_void, List_dynamic);
expect(false, List_void, List_Null);
expect(false, List_void, List_Function);
expect(true, List_Null, List_Object);
expect(true, List_Null, List_void);
expect(true, List_Null, List_dynamic);
expect(true, List_Null, List_Null);
expect(true, List_Null, List_Function);
expect(true, List_Function, List_Object);
expect(true, List_Function, List_void);
expect(true, List_Function, List_dynamic);
expect(false, List_Function, List_Null);
expect(true, List_Function, List_Function);
// Return type are now covariant.
expect(true, returnNum, returnNum);
expect(false, returnNum, returnInt);
expect(true, returnNum, returnVoid);
expect(true, returnNum, returnObject);
expect(true, returnInt, returnNum);
expect(true, returnInt, returnInt);
expect(true, returnInt, returnVoid);
expect(true, returnInt, returnObject);
expect(false, returnVoid, returnNum);
expect(false, returnVoid, returnInt);
expect(true, returnVoid, returnVoid);
expect(true, returnVoid, returnObject);
expect(false, returnObject, returnNum);
expect(false, returnObject, returnInt);
expect(true, returnObject, returnVoid);
expect(true, returnObject, returnObject);
// Arguments types are now contravariant.
expect(true, takeNum, takeNum);
expect(true, takeNum, takeInt);
expect(false, takeNum, takeVoid);
expect(false, takeNum, takeObject);
expect(false, takeInt, takeNum);
expect(true, takeInt, takeInt);
expect(false, takeInt, takeVoid);
expect(false, takeInt, takeObject);
expect(true, takeVoid, takeNum);
expect(true, takeVoid, takeInt);
expect(true, takeVoid, takeVoid);
expect(true, takeVoid, takeObject);
expect(true, takeObject, takeNum);
expect(true, takeObject, takeInt);
expect(true, takeObject, takeVoid);
expect(true, takeObject, takeObject);
});
}