| // 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_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_component.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(TypeSchemaEnvironmentTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class TypeSchemaEnvironmentTest { |
| static const UnknownType unknownType = const UnknownType(); |
| |
| static const DynamicType dynamicType = const DynamicType(); |
| |
| static const VoidType voidType = const VoidType(); |
| |
| static const BottomType bottomType = const BottomType(); |
| |
| final testLib = new Library(Uri.parse('org-dartlang:///test.dart')); |
| |
| Component component; |
| |
| CoreTypes coreTypes; |
| |
| TypeSchemaEnvironmentTest() { |
| component = createMockSdkComponent(); |
| component.libraries.add(testLib..parent = component); |
| coreTypes = new CoreTypes(component); |
| } |
| |
| InterfaceType get doubleType => coreTypes.doubleClass.rawType; |
| |
| InterfaceType get functionType => coreTypes.functionClass.rawType; |
| |
| InterfaceType get intType => coreTypes.intClass.rawType; |
| |
| Class get iterableClass => coreTypes.iterableClass; |
| |
| Class get listClass => coreTypes.listClass; |
| |
| Class get mapClass => coreTypes.mapClass; |
| |
| InterfaceType get nullType => coreTypes.nullClass.rawType; |
| |
| InterfaceType get numType => coreTypes.numClass.rawType; |
| |
| Class get objectClass => coreTypes.objectClass; |
| |
| InterfaceType get objectType => objectClass.rawType; |
| |
| void test_addLowerBound() { |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var C = |
| _addClass(_class('C', supertype: A.classNode.asThisSupertype)).rawType; |
| var env = _makeEnv(); |
| var typeConstraint = new TypeConstraint(); |
| expect(typeConstraint.lower, same(unknownType)); |
| env.addLowerBound(typeConstraint, B); |
| expect(typeConstraint.lower, same(B)); |
| env.addLowerBound(typeConstraint, C); |
| expect(typeConstraint.lower, same(A)); |
| } |
| |
| void test_addUpperBound() { |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var C = |
| _addClass(_class('C', supertype: A.classNode.asThisSupertype)).rawType; |
| var env = _makeEnv(); |
| var typeConstraint = new TypeConstraint(); |
| expect(typeConstraint.upper, same(unknownType)); |
| env.addUpperBound(typeConstraint, A); |
| expect(typeConstraint.upper, same(A)); |
| env.addUpperBound(typeConstraint, B); |
| expect(typeConstraint.upper, same(B)); |
| env.addUpperBound(typeConstraint, C); |
| expect(typeConstraint.upper, same(bottomType)); |
| } |
| |
| void test_glb_bottom() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.getGreatestLowerBound(bottomType, A), same(bottomType)); |
| expect(env.getGreatestLowerBound(A, bottomType), same(bottomType)); |
| } |
| |
| void test_glb_function() { |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var env = _makeEnv(); |
| // GLB(() -> A, () -> B) = () -> B |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], A), new FunctionType([], B)), |
| new FunctionType([], B)); |
| // GLB(() -> void, (A, B) -> void) = ([A, B]) -> void |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], voidType), new FunctionType([A, B], voidType)), |
| new FunctionType([A, B], voidType, requiredParameterCount: 0)); |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([A, B], voidType), new FunctionType([], voidType)), |
| new FunctionType([A, B], voidType, requiredParameterCount: 0)); |
| // GLB((A) -> void, (B) -> void) = (A) -> void |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([A], voidType), new FunctionType([B], voidType)), |
| new FunctionType([A], voidType)); |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([B], voidType), new FunctionType([A], voidType)), |
| new FunctionType([A], voidType)); |
| // GLB(({a: A}) -> void, ({b: B}) -> void) = ({a: A, b: B}) -> void |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A)]), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('b', B)])), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A), new NamedType('b', B)])); |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('b', B)]), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A)])), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A), new NamedType('b', B)])); |
| // GLB(({a: A, c: A}) -> void, ({b: B, d: B}) -> void) |
| // = ({a: A, b: B, c: A, d: B}) -> void |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', A), |
| new NamedType('c', A) |
| ]), |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('b', B), |
| new NamedType('d', B) |
| ])), |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', A), |
| new NamedType('b', B), |
| new NamedType('c', A), |
| new NamedType('d', B) |
| ])); |
| // GLB(({a: A, b: B}) -> void, ({a: B, b: A}) -> void) |
| // = ({a: A, b: A}) -> void |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', A), |
| new NamedType('b', B) |
| ]), |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', B), |
| new NamedType('b', A) |
| ])), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A), new NamedType('b', A)])); |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', B), |
| new NamedType('b', A) |
| ]), |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', A), |
| new NamedType('b', B) |
| ])), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A), new NamedType('b', A)])); |
| // GLB((B, {a: A}) -> void, (B) -> void) = (B, {a: A}) -> void |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([B], voidType, |
| namedParameters: [new NamedType('a', A)]), |
| new FunctionType([B], voidType)), |
| new FunctionType([B], voidType, |
| namedParameters: [new NamedType('a', A)])); |
| // GLB(({a: A}) -> void, (B) -> void) = bottom |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A)]), |
| new FunctionType([B], voidType)), |
| same(bottomType)); |
| // GLB(({a: A}) -> void, ([B]) -> void) = bottom |
| expect( |
| env.getGreatestLowerBound( |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A)]), |
| new FunctionType([B], voidType, requiredParameterCount: 0)), |
| same(bottomType)); |
| } |
| |
| void test_glb_identical() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.getGreatestLowerBound(A, A), same(A)); |
| expect(env.getGreatestLowerBound(new InterfaceType(A.classNode), A), A); |
| } |
| |
| void test_glb_subtype() { |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var env = _makeEnv(); |
| expect(env.getGreatestLowerBound(A, B), same(B)); |
| expect(env.getGreatestLowerBound(B, A), same(B)); |
| } |
| |
| void test_glb_top() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.getGreatestLowerBound(dynamicType, A), same(A)); |
| expect(env.getGreatestLowerBound(A, dynamicType), same(A)); |
| expect(env.getGreatestLowerBound(objectType, A), same(A)); |
| expect(env.getGreatestLowerBound(A, objectType), same(A)); |
| expect(env.getGreatestLowerBound(voidType, A), same(A)); |
| expect(env.getGreatestLowerBound(A, voidType), same(A)); |
| } |
| |
| void test_glb_unknown() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.getGreatestLowerBound(A, unknownType), same(A)); |
| expect(env.getGreatestLowerBound(unknownType, A), same(A)); |
| } |
| |
| void test_glb_unrelated() { |
| var A = _addClass(_class('A')).rawType; |
| var B = _addClass(_class('B')).rawType; |
| var env = _makeEnv(); |
| expect(env.getGreatestLowerBound(A, B), same(bottomType)); |
| } |
| |
| void test_inferGenericFunctionOrType() { |
| var env = _makeEnv(); |
| { |
| // Test an instantiation of [1, 2.0] with no context. This should infer |
| // as List<?> during downwards inference. |
| var inferredTypes = <DartType>[unknownType]; |
| TypeParameterType T = listClass.thisType.typeArguments[0]; |
| env.inferGenericFunctionOrType( |
| listClass.thisType, [T.parameter], null, null, null, inferredTypes); |
| expect(inferredTypes[0], unknownType); |
| // And upwards inference should refine it to List<num>. |
| env.inferGenericFunctionOrType(listClass.thisType, [T.parameter], [T, T], |
| [intType, doubleType], null, inferredTypes); |
| expect(inferredTypes[0], numType); |
| } |
| { |
| // Test an instantiation of [1, 2.0] with a context of List<Object>. This |
| // should infer as List<Object> during downwards inference. |
| var inferredTypes = <DartType>[unknownType]; |
| TypeParameterType T = listClass.thisType.typeArguments[0]; |
| env.inferGenericFunctionOrType(listClass.thisType, [T.parameter], null, |
| null, _list(objectType), inferredTypes); |
| expect(inferredTypes[0], objectType); |
| // And upwards inference should preserve the type. |
| env.inferGenericFunctionOrType(listClass.thisType, [T.parameter], [T, T], |
| [intType, doubleType], _list(objectType), inferredTypes); |
| expect(inferredTypes[0], objectType); |
| } |
| } |
| |
| void test_inferTypeFromConstraints_applyBound() { |
| // class A<T extends num> {} |
| var T = new TypeParameter('T', numType); |
| _addClass(_class('A', typeParameters: [T])).thisType; |
| var env = _makeEnv(); |
| { |
| // With no constraints: |
| var constraints = {T: new TypeConstraint()}; |
| // Downward inference should infer A<?> |
| var inferredTypes = <DartType>[unknownType]; |
| env.inferTypeFromConstraints(constraints, [T], inferredTypes, |
| downwardsInferPhase: true); |
| expect(inferredTypes[0], unknownType); |
| // Upward inference should infer A<num> |
| env.inferTypeFromConstraints(constraints, [T], inferredTypes); |
| expect(inferredTypes[0], numType); |
| } |
| { |
| // With an upper bound of Object: |
| var constraints = {T: _makeConstraint(upper: objectType)}; |
| // Downward inference should infer A<num> |
| var inferredTypes = <DartType>[unknownType]; |
| env.inferTypeFromConstraints(constraints, [T], inferredTypes, |
| downwardsInferPhase: true); |
| expect(inferredTypes[0], numType); |
| // Upward inference should infer A<num> |
| env.inferTypeFromConstraints(constraints, [T], inferredTypes); |
| expect(inferredTypes[0], numType); |
| // Upward inference should still infer A<num> even if there are more |
| // constraints now, because num was finalized during downward inference. |
| constraints = {T: _makeConstraint(lower: intType, upper: intType)}; |
| env.inferTypeFromConstraints(constraints, [T], inferredTypes); |
| expect(inferredTypes[0], numType); |
| } |
| } |
| |
| void test_inferTypeFromConstraints_simple() { |
| var env = _makeEnv(); |
| var T = listClass.typeParameters[0]; |
| // With an upper bound of List<?>: |
| var constraints = {T: _makeConstraint(upper: _list(unknownType))}; |
| // Downwards inference should infer List<List<?>> |
| var inferredTypes = <DartType>[unknownType]; |
| env.inferTypeFromConstraints(constraints, [T], inferredTypes, |
| downwardsInferPhase: true); |
| expect(inferredTypes[0], _list(unknownType)); |
| // Upwards inference should refine that to List<List<dynamic>> |
| env.inferTypeFromConstraints(constraints, [T], inferredTypes); |
| expect(inferredTypes[0], _list(dynamicType)); |
| } |
| |
| void test_lub_classic() { |
| // Make the class hierarchy: |
| // |
| // Object |
| // | |
| // A |
| // /| |
| // B C |
| // |X| |
| // D E |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var C = |
| _addClass(_class('C', supertype: A.classNode.asThisSupertype)).rawType; |
| var D = _addClass(_class('D', implementedTypes: [ |
| B.classNode.asThisSupertype, |
| C.classNode.asThisSupertype |
| ])).rawType; |
| var E = _addClass(_class('E', implementedTypes: [ |
| B.classNode.asThisSupertype, |
| C.classNode.asThisSupertype |
| ])).rawType; |
| var env = _makeEnv(); |
| expect(env.getLeastUpperBound(D, E), A); |
| } |
| |
| void test_lub_commonClass() { |
| var env = _makeEnv(); |
| expect(env.getLeastUpperBound(_list(intType), _list(doubleType)), |
| _list(numType)); |
| } |
| |
| void test_lub_function() { |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var env = _makeEnv(); |
| // LUB(() -> A, () -> B) = () -> A |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], A), new FunctionType([], B)), |
| new FunctionType([], A)); |
| // LUB(([A]) -> void, (A) -> void) = Function |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([A], voidType, requiredParameterCount: 0), |
| new FunctionType([A], voidType)), |
| functionType); |
| // LUB(() -> void, (A, B) -> void) = Function |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], voidType), new FunctionType([A, B], voidType)), |
| functionType); |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([A, B], voidType), new FunctionType([], voidType)), |
| functionType); |
| // LUB((A) -> void, (B) -> void) = (B) -> void |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([A], voidType), new FunctionType([B], voidType)), |
| new FunctionType([B], voidType)); |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([B], voidType), new FunctionType([A], voidType)), |
| new FunctionType([B], voidType)); |
| // LUB(({a: A}) -> void, ({b: B}) -> void) = () -> void |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A)]), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('b', B)])), |
| new FunctionType([], voidType)); |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('b', B)]), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A)])), |
| new FunctionType([], voidType)); |
| // LUB(({a: A, c: A}) -> void, ({b: B, d: B}) -> void) = () -> void |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', A), |
| new NamedType('c', A) |
| ]), |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('b', B), |
| new NamedType('d', B) |
| ])), |
| new FunctionType([], voidType)); |
| // LUB(({a: A, b: B}) -> void, ({a: B, b: A}) -> void) |
| // = ({a: B, b: B}) -> void |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', A), |
| new NamedType('b', B) |
| ]), |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', B), |
| new NamedType('b', A) |
| ])), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', B), new NamedType('b', B)])); |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', B), |
| new NamedType('b', A) |
| ]), |
| new FunctionType([], voidType, |
| namedParameters: [ |
| new NamedType('a', A), |
| new NamedType('b', B) |
| ])), |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', B), new NamedType('b', B)])); |
| // LUB((B, {a: A}) -> void, (B) -> void) = (B) -> void |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([B], voidType, |
| namedParameters: [new NamedType('a', A)]), |
| new FunctionType([B], voidType)), |
| new FunctionType([B], voidType)); |
| // LUB(({a: A}) -> void, (B) -> void) = Function |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A)]), |
| new FunctionType([B], voidType)), |
| functionType); |
| // GLB(({a: A}) -> void, ([B]) -> void) = () -> void |
| expect( |
| env.getLeastUpperBound( |
| new FunctionType([], voidType, |
| namedParameters: [new NamedType('a', A)]), |
| new FunctionType([B], voidType, requiredParameterCount: 0)), |
| new FunctionType([], voidType)); |
| } |
| |
| void test_lub_identical() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.getLeastUpperBound(A, A), same(A)); |
| expect(env.getLeastUpperBound(new InterfaceType(A.classNode), A), A); |
| } |
| |
| void test_lub_sameClass() { |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var env = _makeEnv(); |
| expect(env.getLeastUpperBound(_map(A, B), _map(B, A)), _map(A, A)); |
| } |
| |
| void test_lub_subtype() { |
| var env = _makeEnv(); |
| expect(env.getLeastUpperBound(_list(intType), _iterable(numType)), |
| _iterable(numType)); |
| expect(env.getLeastUpperBound(_iterable(numType), _list(intType)), |
| _iterable(numType)); |
| } |
| |
| void test_lub_top() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.getLeastUpperBound(dynamicType, A), same(dynamicType)); |
| expect(env.getLeastUpperBound(A, dynamicType), same(dynamicType)); |
| expect(env.getLeastUpperBound(objectType, A), same(objectType)); |
| expect(env.getLeastUpperBound(A, objectType), same(objectType)); |
| expect(env.getLeastUpperBound(voidType, A), same(voidType)); |
| expect(env.getLeastUpperBound(A, voidType), same(voidType)); |
| expect(env.getLeastUpperBound(dynamicType, objectType), same(dynamicType)); |
| // TODO(paulberry): see dartbug.com/28513. |
| expect(env.getLeastUpperBound(objectType, dynamicType), same(objectType)); |
| expect(env.getLeastUpperBound(dynamicType, voidType), same(dynamicType)); |
| expect(env.getLeastUpperBound(voidType, dynamicType), same(dynamicType)); |
| expect(env.getLeastUpperBound(objectType, voidType), same(voidType)); |
| expect(env.getLeastUpperBound(voidType, objectType), same(voidType)); |
| } |
| |
| void test_lub_typeParameter() { |
| var T = new TypeParameterType(new TypeParameter('T')); |
| T.parameter.bound = _list(T); |
| var U = new TypeParameterType(new TypeParameter('U')); |
| U.parameter.bound = _list(bottomType); |
| var env = _makeEnv(); |
| // LUB(T, T) = T |
| expect(env.getLeastUpperBound(T, T), same(T)); |
| // LUB(T, List<Bottom>) = LUB(List<Object>, List<Bottom>) = List<Object> |
| expect(env.getLeastUpperBound(T, _list(bottomType)), _list(objectType)); |
| expect(env.getLeastUpperBound(_list(bottomType), T), _list(objectType)); |
| // LUB(T, U) = LUB(List<Object>, U) = LUB(List<Object>, List<Bottom>) |
| // = List<Object> |
| expect(env.getLeastUpperBound(T, U), _list(objectType)); |
| expect(env.getLeastUpperBound(U, T), _list(objectType)); |
| } |
| |
| void test_lub_unknown() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.getGreatestLowerBound(A, unknownType), same(A)); |
| expect(env.getGreatestLowerBound(unknownType, A), same(A)); |
| } |
| |
| void test_solveTypeConstraint() { |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var env = _makeEnv(); |
| // Solve(? <: T <: ?) => ? |
| expect(env.solveTypeConstraint(_makeConstraint()), same(unknownType)); |
| // Solve(? <: T <: ?, grounded) => dynamic |
| expect(env.solveTypeConstraint(_makeConstraint(), grounded: true), |
| dynamicType); |
| // Solve(A <: T <: ?) => A |
| expect(env.solveTypeConstraint(_makeConstraint(lower: A)), A); |
| // Solve(A <: T <: ?, grounded) => A |
| expect( |
| env.solveTypeConstraint(_makeConstraint(lower: A), grounded: true), A); |
| // Solve(A<?> <: T <: ?) => A<?> |
| expect( |
| env.solveTypeConstraint(_makeConstraint( |
| lower: new InterfaceType(A.classNode, [unknownType]))), |
| new InterfaceType(A.classNode, [unknownType])); |
| // Solve(A<?> <: T <: ?, grounded) => A<Null> |
| expect( |
| env.solveTypeConstraint( |
| _makeConstraint( |
| lower: new InterfaceType(A.classNode, [unknownType])), |
| grounded: true), |
| new InterfaceType(A.classNode, [nullType])); |
| // Solve(? <: T <: A) => A |
| expect(env.solveTypeConstraint(_makeConstraint(upper: A)), A); |
| // Solve(? <: T <: A, grounded) => A |
| expect( |
| env.solveTypeConstraint(_makeConstraint(upper: A), grounded: true), A); |
| // Solve(? <: T <: A<?>) => A<?> |
| expect( |
| env.solveTypeConstraint(_makeConstraint( |
| upper: new InterfaceType(A.classNode, [unknownType]))), |
| new InterfaceType(A.classNode, [unknownType])); |
| // Solve(? <: T <: A<?>, grounded) => A<dynamic> |
| expect( |
| env.solveTypeConstraint( |
| _makeConstraint( |
| upper: new InterfaceType(A.classNode, [unknownType])), |
| grounded: true), |
| new InterfaceType(A.classNode, [dynamicType])); |
| // Solve(B <: T <: A) => B |
| expect(env.solveTypeConstraint(_makeConstraint(lower: B, upper: A)), B); |
| // Solve(B <: T <: A, grounded) => B |
| expect( |
| env.solveTypeConstraint(_makeConstraint(lower: B, upper: A), |
| grounded: true), |
| B); |
| // Solve(B<?> <: T <: A) => A |
| expect( |
| env.solveTypeConstraint(_makeConstraint( |
| lower: new InterfaceType(B.classNode, [unknownType]), upper: A)), |
| A); |
| // Solve(B<?> <: T <: A, grounded) => A |
| expect( |
| env.solveTypeConstraint( |
| _makeConstraint( |
| lower: new InterfaceType(B.classNode, [unknownType]), upper: A), |
| grounded: true), |
| A); |
| // Solve(B <: T <: A<?>) => B |
| expect( |
| env.solveTypeConstraint(_makeConstraint( |
| lower: B, upper: new InterfaceType(A.classNode, [unknownType]))), |
| B); |
| // Solve(B <: T <: A<?>, grounded) => B |
| expect( |
| env.solveTypeConstraint( |
| _makeConstraint( |
| lower: B, upper: new InterfaceType(A.classNode, [unknownType])), |
| grounded: true), |
| B); |
| // Solve(B<?> <: T <: A<?>) => B<?> |
| expect( |
| env.solveTypeConstraint(_makeConstraint( |
| lower: new InterfaceType(B.classNode, [unknownType]), |
| upper: new InterfaceType(A.classNode, [unknownType]))), |
| new InterfaceType(B.classNode, [unknownType])); |
| // Solve(B<?> <: T <: A<?>) => B<Null> |
| expect( |
| env.solveTypeConstraint( |
| _makeConstraint( |
| lower: new InterfaceType(B.classNode, [unknownType]), |
| upper: new InterfaceType(A.classNode, [unknownType])), |
| grounded: true), |
| new InterfaceType(B.classNode, [nullType])); |
| } |
| |
| void test_typeConstraint_default() { |
| var typeConstraint = new TypeConstraint(); |
| expect(typeConstraint.lower, same(unknownType)); |
| expect(typeConstraint.upper, same(unknownType)); |
| } |
| |
| void test_typeSatisfiesConstraint() { |
| var A = _addClass(_class('A')).rawType; |
| var B = |
| _addClass(_class('B', supertype: A.classNode.asThisSupertype)).rawType; |
| var C = |
| _addClass(_class('C', supertype: B.classNode.asThisSupertype)).rawType; |
| var D = |
| _addClass(_class('D', supertype: C.classNode.asThisSupertype)).rawType; |
| var E = |
| _addClass(_class('E', supertype: D.classNode.asThisSupertype)).rawType; |
| var env = _makeEnv(); |
| var typeConstraint = _makeConstraint(upper: B, lower: D); |
| expect(env.typeSatisfiesConstraint(A, typeConstraint), isFalse); |
| expect(env.typeSatisfiesConstraint(B, typeConstraint), isTrue); |
| expect(env.typeSatisfiesConstraint(C, typeConstraint), isTrue); |
| expect(env.typeSatisfiesConstraint(D, typeConstraint), isTrue); |
| expect(env.typeSatisfiesConstraint(E, typeConstraint), isFalse); |
| } |
| |
| void test_unknown_at_bottom() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.isSubtypeOf(unknownType, A), isTrue); |
| } |
| |
| void test_unknown_at_top() { |
| var A = _addClass(_class('A')).rawType; |
| var env = _makeEnv(); |
| expect(env.isSubtypeOf(A, unknownType), isTrue); |
| } |
| |
| Class _addClass(Class c) { |
| testLib.addClass(c); |
| return c; |
| } |
| |
| 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 elementType) => |
| new InterfaceType(iterableClass, [elementType]); |
| |
| DartType _list(DartType elementType) => |
| new InterfaceType(listClass, [elementType]); |
| |
| TypeConstraint _makeConstraint( |
| {DartType lower: const UnknownType(), |
| DartType upper: const UnknownType()}) { |
| return new TypeConstraint() |
| ..lower = lower |
| ..upper = upper; |
| } |
| |
| TypeSchemaEnvironment _makeEnv() { |
| return new TypeSchemaEnvironment( |
| coreTypes, new ClassHierarchy(component), true); |
| } |
| |
| DartType _map(DartType key, DartType value) => |
| new InterfaceType(mapClass, [key, value]); |
| } |