blob: d51d345a68a6dd0e56dee49431548bfed725f3f3 [file] [log] [blame]
// Copyright (c) 2017, 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:front_end/src/fasta/type_inference/type_constraint_gatherer.dart';
import 'package:front_end/src/fasta/type_inference/type_schema.dart';
import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/testing/mock_sdk_program.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(TypeConstraintGathererTest);
});
}
@reflectiveTest
class TypeConstraintGathererTest {
static const UnknownType unknownType = const UnknownType();
static const DynamicType dynamicType = const DynamicType();
static const VoidType voidType = const VoidType();
final testLib =
new Library(Uri.parse('org-dartlang:///test.dart'), name: 'lib');
Program program;
CoreTypes coreTypes;
TypeParameterType T1;
TypeParameterType T2;
Class classP;
Class classQ;
TypeConstraintGathererTest() {
program = createMockSdkProgram();
program.libraries.add(testLib..parent = program);
coreTypes = new CoreTypes(program);
T1 = new TypeParameterType(new TypeParameter('T1', objectType));
T2 = new TypeParameterType(new TypeParameter('T2', objectType));
classP = _addClass(_class('P'));
classQ = _addClass(_class('Q'));
}
Class get functionClass => coreTypes.functionClass;
InterfaceType get functionType => functionClass.rawType;
Class get iterableClass => coreTypes.iterableClass;
Class get listClass => coreTypes.listClass;
Class get mapClass => coreTypes.mapClass;
InterfaceType get nullType => coreTypes.nullClass.rawType;
Class get objectClass => coreTypes.objectClass;
InterfaceType get objectType => objectClass.rawType;
InterfaceType get P => classP.rawType;
InterfaceType get Q => classQ.rawType;
void test_any_subtype_parameter() {
_checkConstraints(Q, T1, ['lib::Q <: T1']);
}
void test_any_subtype_top() {
_checkConstraints(P, dynamicType, []);
_checkConstraints(P, objectType, []);
_checkConstraints(P, voidType, []);
}
void test_any_subtype_unknown() {
_checkConstraints(P, unknownType, []);
_checkConstraints(T1, unknownType, []);
}
void test_different_classes() {
_checkConstraints(_list(T1), _iterable(Q), ['T1 <: lib::Q']);
_checkConstraints(_iterable(T1), _list(Q), null);
}
void test_equal_types() {
_checkConstraints(P, P, []);
}
void test_function_generic() {
var T = new TypeParameterType(new TypeParameter('T', objectType));
var U = new TypeParameterType(new TypeParameter('U', objectType));
// <T>() -> dynamic <: () -> dynamic, never
_checkConstraints(
new FunctionType([], dynamicType, typeParameters: [T.parameter]),
new FunctionType([], dynamicType),
null);
// () -> dynamic <: <T>() -> dynamic, never
_checkConstraints(new FunctionType([], dynamicType),
new FunctionType([], dynamicType, typeParameters: [T.parameter]), null);
// <T>(T) -> T <: <U>(U) -> U, always
_checkConstraints(new FunctionType([T], T, typeParameters: [T.parameter]),
new FunctionType([U], U, typeParameters: [U.parameter]), []);
}
void test_function_parameter_mismatch() {
// (P) -> dynamic <: () -> dynamic, never
_checkConstraints(new FunctionType([P], dynamicType),
new FunctionType([], dynamicType), null);
// () -> dynamic <: (P) -> dynamic, never
_checkConstraints(new FunctionType([], dynamicType),
new FunctionType([P], dynamicType), null);
// ([P]) -> dynamic <: () -> dynamic, always
_checkConstraints(
new FunctionType([P], dynamicType, requiredParameterCount: 0),
new FunctionType([], dynamicType), []);
// () -> dynamic <: ([P]) -> dynamic, never
_checkConstraints(new FunctionType([], dynamicType),
new FunctionType([P], dynamicType, requiredParameterCount: 0), null);
// ({x: P}) -> dynamic <: () -> dynamic, always
_checkConstraints(
new FunctionType([], dynamicType,
namedParameters: [new NamedType('x', P)]),
new FunctionType([], dynamicType),
[]);
// () -> dynamic !<: ({x: P}) -> dynamic, never
_checkConstraints(
new FunctionType([], dynamicType),
new FunctionType([], dynamicType,
namedParameters: [new NamedType('x', P)]),
null);
}
void test_function_parameter_types() {
// (T1) -> dynamic <: (Q) -> dynamic, under constraint Q <: T1
_checkConstraints(new FunctionType([T1], dynamicType),
new FunctionType([Q], dynamicType), ['lib::Q <: T1']);
// ({x: T1}) -> dynamic <: ({x: Q}) -> dynamic, under constraint Q <: T1
_checkConstraints(
new FunctionType([], dynamicType,
namedParameters: [new NamedType('x', T1)]),
new FunctionType([], dynamicType,
namedParameters: [new NamedType('x', Q)]),
['lib::Q <: T1']);
}
void test_function_return_type() {
// () -> T1 <: () -> Q, under constraint T1 <: Q
_checkConstraints(
new FunctionType([], T1), new FunctionType([], Q), ['T1 <: lib::Q']);
// () -> P <: () -> void, always
_checkConstraints(
new FunctionType([], P), new FunctionType([], voidType), []);
// () -> void <: () -> P, never
_checkConstraints(
new FunctionType([], voidType), new FunctionType([], P), null);
}
void test_function_trivial_cases() {
var F = new FunctionType([], dynamicType);
// () -> dynamic <: dynamic, always
_checkConstraints(F, dynamicType, []);
// () -> dynamic <: Function, always
_checkConstraints(F, functionType, []);
// () -> dynamic <: Object, always
_checkConstraints(F, objectType, []);
}
void test_nonInferredParameter_subtype_any() {
var U = new TypeParameterType(new TypeParameter('U', _list(P)));
_checkConstraints(U, _list(T1), ['lib::P <: T1']);
}
void test_null_subtype_any() {
_checkConstraints(nullType, T1, ['dart.core::Null <: T1']);
_checkConstraints(nullType, Q, []);
}
void test_parameter_subtype_any() {
_checkConstraints(T1, Q, ['T1 <: lib::Q']);
}
void test_same_classes() {
_checkConstraints(_list(T1), _list(Q), ['T1 <: lib::Q']);
}
void test_typeParameters() {
_checkConstraints(
_map(T1, T2), _map(P, Q), ['T1 <: lib::P', 'T2 <: lib::Q']);
}
void test_unknown_subtype_any() {
_checkConstraints(unknownType, Q, []);
_checkConstraints(unknownType, T1, []);
}
Class _addClass(Class c) {
testLib.addClass(c);
return c;
}
void _checkConstraints(
DartType a, DartType b, List<String> expectedConstraints) {
var typeSchemaEnvironment =
new TypeSchemaEnvironment(coreTypes, new ClassHierarchy(program), true);
var typeConstraintGatherer = new TypeConstraintGatherer(
typeSchemaEnvironment, [T1.parameter, T2.parameter]);
var constraints = typeConstraintGatherer.trySubtypeMatch(a, b)
? typeConstraintGatherer.computeConstraints()
: null;
if (expectedConstraints == null) {
expect(constraints, isNull);
return;
}
expect(constraints, isNotNull);
var constraintStrings = <String>[];
constraints.forEach((t, constraint) {
if (constraint.lower is! UnknownType ||
constraint.upper is! UnknownType) {
var s = t.name;
if (constraint.lower is! UnknownType) {
s = '${typeSchemaToString(constraint.lower)} <: $s';
}
if (constraint.upper is! UnknownType) {
s = '$s <: ${typeSchemaToString(constraint.upper)}';
}
constraintStrings.add(s);
}
});
expect(constraintStrings, unorderedEquals(expectedConstraints));
}
Class _class(String name,
{Supertype supertype,
List<TypeParameter> typeParameters,
List<Supertype> implementedTypes}) {
return new Class(
name: name,
supertype: supertype ?? objectClass.asThisSupertype,
typeParameters: typeParameters,
implementedTypes: implementedTypes);
}
DartType _iterable(DartType element) =>
new InterfaceType(iterableClass, [element]);
DartType _list(DartType element) => new InterfaceType(listClass, [element]);
DartType _map(DartType key, DartType value) =>
new InterfaceType(mapClass, [key, value]);
}