anchor.diff:      // class A
       // class B
       // class C
  -    ClassElementImpl classA = ElementFactory.classElement2("A");
  -    ClassElementImpl classB = ElementFactory.classElement2("B");
  +    var classA = _class(name: 'A');
  +    var classB = _class(name: 'B');
       _checkGreatestLowerBound(classA.type, classB.type, bottomType);
     }
   
     void test_void() {
       List<DartType> types = [
         bottomType,
  -      simpleFunctionType,
  -      ElementFactory.classElement2('A', []).type,
  -      ElementFactory.typeParameterElement('T').type
  +      _functionType(),
  +      _class(name: 'A').type,
  +      _typeParameter('T').type
       ];
       for (DartType type in types) {
         _checkGreatestLowerBound(
  -        _functionType(required: [], returns: voidType),
  -        _functionType(required: [], returns: type),
  -        _functionType(required: [], returns: type),
  +        _functionType(returns: voidType),
  +        _functionType(returns: type),
  +        _functionType(returns: type),
         );
       }
     }
  
positive.code:       numType,
        stringType,
        functionType,
        interfaceType
      ];
      _checkGroups(bottomType, equivalents: equivalents, supertypes: supertypes);
    }
  
    void test_call_method() {
      ClassElementImpl classBottom = ElementFactory.classElement2("Bottom");
      MethodElement methodBottom =
          ElementFactory.methodElement("call", objectType, <DartType>[intType]);
      classBottom.methods = <MethodElement>[methodBottom];
  
      DartType top =
          TypeBuilder.function(required: <DartType>[intType], result: objectType);
      InterfaceType bottom = classBottom.type;
  
      _checkIsNotSubtypeOf(bottom, top);
    }
  
    void test_classes() {
      ClassElement classTop = ElementFactory.classElement2("A");
      ClassElement classLeft = ElementFactory.classElement("B", classTop.type);
      ClassElement classRight = ElementFactory.classElement("C", classTop.type);
      ClassElement classBottom = ElementFactory.classElement("D", classLeft.type)
        ..interfaces = <InterfaceType>[classRight.type];
      InterfaceType top = classTop.type;
      InterfaceType left = classLeft.type;
      InterfaceType right = classRight.type;
      InterfaceType bottom = classBottom.type;
  
      _checkLattice(top, left, right, bottom);
    }
  
negative.code:       returns ?? voidType,
        typeFormals ?? const <TypeParameterElement>[],
        parameters,
        nullabilitySuffix: nullabilitySuffix,
      );
    }
  
    InterfaceType _interfaceType(
      ClassElement element, {
      List<DartType> typeArguments = const [],
      NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star,
    }) {
      return InterfaceTypeImpl.explicit(
        element,
        typeArguments,
        nullabilitySuffix: nullabilitySuffix,
      );
    }
  
    MethodElementImpl _method(
      String name,
      DartType returnType, {
      List<TypeParameterElement> typeFormals = const [],
      List<ParameterElement> parameters = const [],
    }) {
      var element = MethodElementImpl(name, 0)
        ..parameters = parameters
        ..returnType = returnType
        ..typeParameters = typeFormals;
      element.type = _typeOfExecutableElement(element);
      return element;
    }
  
    ParameterElement _requiredParameter(String name, DartType type) {
      var parameter = ParameterElementImpl(name, 0);
      parameter.parameterKind = ParameterKind.REQUIRED;
      parameter.type = type;
      return parameter;
    }
  
    /// TODO(scheglov) We should do the opposite - build type in the element.
    /// But build a similar synthetic / structured type.
    FunctionType _typeOfExecutableElement(ExecutableElement element) {
      return FunctionTypeImpl.synthetic(
        element.returnType,
        element.typeParameters,
        element.parameters,
      );
    }
  
    TypeParameterElementImpl _typeParameter(String name, {DartType bound}) {
      var element = TypeParameterElementImpl.synthetic(name);
      element.bound = bound;
      return element;
    }
  
    TypeParameterTypeImpl _typeParameterType(
      TypeParameterElement element, {
      NullabilitySuffix nullabilitySuffix = NullabilitySuffix.star,
    }) {
      return TypeParameterTypeImpl(
        element,
        nullabilitySuffix: nullabilitySuffix,
      );
    }
  }
  
  @reflectiveTest
  class AssignabilityTest extends AbstractTypeSystemTest {
    void test_isAssignableTo_bottom_isBottom() {
      var interfaceType = _class(name: 'A').type;
      List<DartType> interassignable = <DartType>[
        dynamicType,
        objectType,
        intType,
        doubleType,
        numType,
        stringType,
        interfaceType,
        bottomType,
      ];
  
      _checkGroups(bottomType, interassignable: interassignable);
    }
  
    void test_isAssignableTo_call_method() {
      var classBottom = _class(name: 'B');
      var methodBottom = _method(
        'call',
        objectType,
        parameters: [
          _requiredParameter('_', intType),
        ],
      );
      classBottom.methods = <MethodElement>[methodBottom];
  
      var top = _functionType(required: [intType], returns: objectType);
      var bottom = classBottom.type;
  
      _checkIsStrictAssignableTo(bottom, top);
    }
  
    void test_isAssignableTo_classes() {
      var classTop = _class(name: 'A');
      var classLeft = _class(name: 'B', superType: _interfaceType(classTop));
      var classRight = _class(name: 'C', superType: _interfaceType(classTop));
      var classBottom = _class(
        name: 'D',
        superType: _interfaceType(classLeft),
        interfaces: [_interfaceType(classRight)],
      );
      var top = _interfaceType(classTop);
      var left = _interfaceType(classLeft);
      var right = _interfaceType(classRight);
      var bottom = _interfaceType(classBottom);
  
      _checkLattice(top, left, right, bottom);
    }
  
    void test_isAssignableTo_double() {
      var interfaceType = _class(name: 'A').type;
      List<DartType> interassignable = <DartType>[
        dynamicType,
        objectType,
        doubleType,
        numType,
        bottomType,
      ];
      List<DartType> unrelated = <DartType>[
        intType,
        stringType,
        interfaceType,
      ];
  
      _checkGroups(doubleType,
          interassignable: interassignable, unrelated: unrelated);
    }
  
    void test_isAssignableTo_dynamic_isTop() {
      var interfaceType = _class(name: 'A').type;
      List<DartType> interassignable = <DartType>[
        dynamicType,
        objectType,
        intType,
        doubleType,
        numType,
        stringType,
        interfaceType,
        bottomType,
      ];
      _checkGroups(dynamicType, interassignable: interassignable);
    }
  
    void test_isAssignableTo_generics() {
      var LClass = _class(name: 'L', typeParameters: [_typeParameter('T')]);
      var LType = LClass.type;
      var MClass = _class(name: 'M', typeParameters: [_typeParameter('T')]);
      var typeParam = MClass.typeParameters[0].type;
      var superType = LType.instantiate(<DartType>[typeParam]);
      MClass.interfaces = <InterfaceType>[superType];
      var MType = MClass.type;
  
      var top = LType.instantiate(<DartType>[dynamicType]);
      var left = MType.instantiate(<DartType>[dynamicType]);
      var right = LType.instantiate(<DartType>[intType]);
      var bottom = MType.instantiate(<DartType>[intType]);
  
      _checkCrossLattice(top, left, right, bottom);
    }
  
    void test_isAssignableTo_int() {
      var interfaceType = _class(name: 'A').type;
      List<DartType> interassignable = <DartType>[
        dynamicType,
        objectType,
        intType,
        numType,
        bottomType,
      ];
      List<DartType> unrelated = <DartType>[
        doubleType,
        stringType,
        interfaceType,
      ];
  
      _checkGroups(intType,
          interassignable: interassignable, unrelated: unrelated);
    }
  
    void test_isAssignableTo_named_optional() {
      var r = _functionType(required: [intType], returns: intType);
      var o = _functionType(optional: [intType], returns: intType);
      var n = _functionType(named: {'x': intType}, returns: intType);
      var rr = _functionType(required: [intType, intType], returns: intType);
      var ro = _functionType(
          required: [intType], optional: [intType], returns: intType);
      var rn = _functionType(
          required: [intType], named: {'x': intType}, returns: intType);
      var oo = _functionType(optional: [intType, intType], returns: intType);
      var nn =
          _functionType(named: {'x': intType, 'y': intType}, returns: intType);
      var nnn = _functionType(
          named: {'x': intType, 'y': intType, 'z': intType}, returns: intType);
  
      _checkGroups(r,
          interassignable: [r, o, ro, rn, oo], unrelated: [n, rr, nn, nnn]);
      _checkGroups(o,
          interassignable: [o, oo], unrelated: [n, rr, ro, rn, nn, nnn]);
      _checkGroups(n,
          interassignable: [n, nn, nnn], unrelated: [r, o, rr, ro, rn, oo]);
      _checkGroups(rr,
          interassignable: [rr, ro, oo], unrelated: [r, o, n, rn, nn, nnn]);
      _checkGroups(ro, interassignable: [ro, oo], unrelated: [o, n, rn, nn, nnn]);
      _checkGroups(rn,
          interassignable: [rn], unrelated: [o, n, rr, ro, oo, nn, nnn]);
      _checkGroups(oo, interassignable: [oo], unrelated: [n, rn, nn, nnn]);
      _checkGroups(nn,
          interassignable: [nn, nnn], unrelated: [r, o, rr, ro, rn, oo]);
      _checkGroups(nnn,
          interassignable: [nnn], unrelated: [r, o, rr, ro, rn, oo]);
    }
  
    void test_isAssignableTo_num() {
      var interfaceType = _class(name: 'A').type;
      List<DartType> interassignable = <DartType>[
        dynamicType,
        objectType,
        numType,
        intType,
        doubleType,
        bottomType,
      ];
      List<DartType> unrelated = <DartType>[
        stringType,
        interfaceType,
      ];
  
      _checkGroups(numType,
          interassignable: interassignable, unrelated: unrelated);
    }
  
    void test_isAssignableTo_simple_function() {
      var top = _functionType(required: [intType], returns: objectType);
      var left = _functionType(required: [intType], returns: intType);
      var right = _functionType(required: [objectType], returns: objectType);
      var bottom = _functionType(required: [objectType], returns: intType);
  
      _checkCrossLattice(top, left, right, bottom);
    }
  
    void test_isAssignableTo_void_functions() {
      var top = _functionType(required: [intType], returns: voidType);
      var bottom = _functionType(required: [objectType], returns: intType);
  
      _checkEquivalent(bottom, top);
    }
  
    void _checkCrossLattice(
        DartType top, DartType left, DartType right, DartType bottom) {
      _checkGroups(top, interassignable: [top, left, right, bottom]);
      _checkGroups(left,
          interassignable: [top, left, bottom], unrelated: [right]);
      _checkGroups(right,
          interassignable: [top, right, bottom], unrelated: [left]);
      _checkGroups(bottom, interassignable: [top, left, right, bottom]);
    }
  
    void _checkEquivalent(DartType type1, DartType type2) {
      _checkIsAssignableTo(type1, type2);
      _checkIsAssignableTo(type2, type1);
    }
  
    void _checkGroups(DartType t1,
        {List<DartType> interassignable, List<DartType> unrelated}) {
      if (interassignable != null) {
        for (DartType t2 in interassignable) {
          _checkEquivalent(t1, t2);
        }
      }
      if (unrelated != null) {
        for (DartType t2 in unrelated) {
          _checkUnrelated(t1, t2);
        }
      }
    }
  
    void _checkIsAssignableTo(DartType type1, DartType type2) {
      expect(typeSystem.isAssignableTo(type1, type2), true);
    }
  
    void _checkIsNotAssignableTo(DartType type1, DartType type2) {
      expect(typeSystem.isAssignableTo(type1, type2), false);
    }
  
    void _checkIsStrictAssignableTo(DartType type1, DartType type2) {
      _checkIsAssignableTo(type1, type2);
      _checkIsNotAssignableTo(type2, type1);
    }
  
    void _checkLattice(
        DartType top, DartType left, DartType right, DartType bottom) {
      _checkGroups(top, interassignable: <DartType>[top, left, right, bottom]);
      _checkGroups(left,
          interassignable: <DartType>[top, left, bottom],
          unrelated: <DartType>[right]);
      _checkGroups(right,
          interassignable: <DartType>[top, right, bottom],
          unrelated: <DartType>[left]);
      _checkGroups(bottom, interassignable: <DartType>[top, left, right, bottom]);
    }
  
    void _checkUnrelated(DartType type1, DartType type2) {
      _checkIsNotAssignableTo(type1, type2);
      _checkIsNotAssignableTo(type2, type1);
    }
  }
  
  /**
   * Base class for testing LUB and GLB in spec and strong mode.
   */
  abstract class BoundTestBase extends AbstractTypeSystemTest {
    void _checkGreatestLowerBound(
        DartType type1, DartType type2, DartType expectedResult) {
      var glb = typeSystem.getGreatestLowerBound(type1, type2);
      expect(glb, expectedResult);
      // Check that the result is a lower bound.
      expect(typeSystem.isSubtypeOf(glb, type1), true);
      expect(typeSystem.isSubtypeOf(glb, type2), true);
      // Check for symmetry while we're at it.  Unfortunately,
      // for function types, the current version of equality
      // does not respect re-ordering of named parameters, so
      // for function types we just check if they are mutual subtypes.
      // https://github.com/dart-lang/sdk/issues/26126
      // TODO(leafp): Fix this.
      glb = typeSystem.getGreatestLowerBound(type2, type1);
      if (glb is FunctionTypeImpl) {
        expect(typeSystem.isSubtypeOf(glb, expectedResult), true);
        expect(typeSystem.isSubtypeOf(expectedResult, glb), true);
      } else {
        expect(glb, expectedResult);
      }
    }
  
    void _checkLeastUpperBound(
        DartType type1, DartType type2, DartType expectedResult) {
      var lub = typeSystem.getLeastUpperBound(type1, type2);
      expect(lub, expectedResult);
      // Check that the result is an upper bound.
      expect(typeSystem.isSubtypeOf(type1, lub), true);
      expect(typeSystem.isSubtypeOf(type2, lub), true);
  
      // Check for symmetry while we're at it.  Unfortunately,
      // for function types, the current version of equality
      // does not respect re-ordering of named parameters, so
      // for function types we just check if they are mutual subtypes.
      // https://github.com/dart-lang/sdk/issues/26126
      // TODO(leafp): Fix this.
      lub = typeSystem.getLeastUpperBound(type2, type1);
      if (lub is FunctionTypeImpl) {
        expect(typeSystem.isSubtypeOf(lub, expectedResult), true);
        expect(typeSystem.isSubtypeOf(expectedResult, lub), true);
      } else {
        expect(lub, expectedResult);
      }
    }
  }
  
  