anchor.diff:      }
       return true;
     }
  -
  -  static bool _compareTypeFormalLists(List<TypeParameterElement> formals1,
  +}
  +
  +/// Helper class that renames the type parameters in two decorated function
  +/// types so that they match.
  +class RenamedDecoratedFunctionTypes {
  +  final DecoratedType returnType1;
  +
  +  final DecoratedType returnType2;
  +
  +  final List<DecoratedType> positionalParameters1;
  +
  +  final List<DecoratedType> positionalParameters2;
  +
  +  final Map<String, DecoratedType> namedParameters1;
  +
  +  final Map<String, DecoratedType> namedParameters2;
  +
  +  RenamedDecoratedFunctionTypes._(
  +      this.returnType1,
  +      this.returnType2,
  +      this.positionalParameters1,
  +      this.positionalParameters2,
  +      this.namedParameters1,
  +      this.namedParameters2);
  +
  +  /// Attempt to find a renaming of the type parameters of [type1] and [type2]
  +  /// (both of which should be function types) such that the generic type
  +  /// parameters match.
  +  ///
  +  /// The callback [boundsMatcher] is used to determine whether type parameter
  +  /// bounds match.
  +  ///
  +  /// If such a renaming can be found, it is returned.  If not, `null` is
  +  /// returned.
  +  static RenamedDecoratedFunctionTypes match(
  +      DecoratedType type1,
  +      DecoratedType type2,
  +      bool Function(DecoratedType, DecoratedType) boundsMatcher) {
  +    if (!_isNeeded(type1.typeFormals, type2.typeFormals)) {
  +      return RenamedDecoratedFunctionTypes._(
  +          type1.returnType,
  +          type2.returnType,
  +          type1.positionalParameters,
  +          type2.positionalParameters,
  +          type1.namedParameters,
  +          type2.namedParameters);
  +    }
  +    // Create a fresh set of type variables and substitute so we can
  +    // compare safely.
  +    var substitution1 = <TypeParameterElement, DecoratedType>{};
  +    var substitution2 = <TypeParameterElement, DecoratedType>{};
  +    var newParameters = <TypeParameterElement>[];
  +    for (int i = 0; i < type1.typeFormals.length; i++) {
  +      var newParameter =
  +          TypeParameterElementImpl.synthetic(type1.typeFormals[i].name);
  +      newParameters.add(newParameter);
  +      var newParameterType =
  +          DecoratedType._forTypeParameterSubstitution(newParameter);
  +      substitution1[type1.typeFormals[i]] = newParameterType;
  +      substitution2[type2.typeFormals[i]] = newParameterType;
  +    }
  +    for (int i = 0; i < type1.typeFormals.length; i++) {
  +      var bound1 = type1.typeFormalBounds[i].substitute(substitution1);
  +      var bound2 = type2.typeFormalBounds[i].substitute(substitution2);
  +      if (!boundsMatcher(bound1, bound2)) return null;
  +      DecoratedType.recordTypeParameterBound(newParameters[i], bound1);
  +    }
  +    var returnType1 = type1.returnType.substitute(substitution1);
  +    var returnType2 = type2.returnType.substitute(substitution2);
  +    var positionalParameters1 =
  +        _substituteList(type1.positionalParameters, substitution1);
  +    var positionalParameters2 =
  +        _substituteList(type2.positionalParameters, substitution2);
  +    var namedParameters1 = _substituteMap(type1.namedParameters, substitution1);
  +    var namedParameters2 = _substituteMap(type2.namedParameters, substitution2);
  +    return RenamedDecoratedFunctionTypes._(
  +        returnType1,
  +        returnType2,
  +        positionalParameters1,
  +        positionalParameters2,
  +        namedParameters1,
  +        namedParameters2);
  +  }
  +
  +  static bool _isNeeded(List<TypeParameterElement> formals1,
         List<TypeParameterElement> formals2) {
  -    if (identical(formals1, formals2)) return true;
  -    if (formals1.length != formals2.length) return false;
  +    if (identical(formals1, formals2)) return false;
  +    if (formals1.length != formals2.length) return true;
       for (int i = 0; i < formals1.length; i++) {
  -      if (!identical(formals1[i], formals2[i])) return false;
  -    }
  -    return true;
  +      if (!identical(formals1[i], formals2[i])) return true;
  +    }
  +    return false;
  +  }
  +
  +  static List<DecoratedType> _substituteList(List<DecoratedType> list,
  +      Map<TypeParameterElement, DecoratedType> substitution) {
  +    return list.map((t) => t.substitute(substitution)).toList();
  +  }
  +
  +  static Map<String, DecoratedType> _substituteMap(
  +      Map<String, DecoratedType> map,
  +      Map<TypeParameterElement, DecoratedType> substitution) {
  +    var result = <String, DecoratedType>{};
  +    for (var entry in map.entries) {
  +      result[entry.key] = entry.value.substitute(substitution);
  +    }
  +    return result;
     }
   }
  
positive.code:         namedParameters: newNamedParameters);
    }
  
    List<DecoratedType> _substituteList(List<DecoratedType> list,
        Map<TypeParameterElement, DecoratedType> substitution) {
      return list.map((t) => t.substitute(substitution)).toList();
    }
  
    Map<String, DecoratedType> _substituteMap(Map<String, DecoratedType> map,
        Map<TypeParameterElement, DecoratedType> substitution) {
      var result = <String, DecoratedType>{};
      for (var entry in map.entries) {
        result[entry.key] = entry.value.substitute(substitution);
      }
      return result;
    }
  
    /// Retrieves the decorated bound of the given [typeParameter].
    ///
    /// [typeParameter] must have an enclosing element of `null`.  Type parameters
  
negative.code:         if (thisType.typeFormals.length != otherType.typeFormals.length) {
            return false;
          }
          var renamed = RenamedDecoratedFunctionTypes.match(
              this, other, (bound1, bound2) => bound1 == bound2);
          if (renamed == null) return false;
          if (renamed.returnType1 != renamed.returnType2) return false;
          if (!_compareLists(
              renamed.positionalParameters1, renamed.positionalParameters2)) {
            return false;
          }
          if (!_compareMaps(renamed.namedParameters1, renamed.namedParameters2)) {
            return false;
          }
          return true;
  