Issue 34699. Use type parameter variance when instantiate to bounds.

Bug: https://github.com/dart-lang/sdk/issues/34699
Bug: https://github.com/dart-lang/sdk/issues/43524
Change-Id: I8fe14c59fff17f3e4ebee62fbd7be2b45172a595
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164860
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 1f4fe07..e429f2b 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -85,7 +85,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 111;
+  static const int DATA_VERSION = 112;
 
   /// The length of the list returned by [_computeDeclaredVariablesSignature].
   static const int _declaredVariablesSignatureLength = 4;
diff --git a/pkg/analyzer/lib/src/dart/element/replacement_visitor.dart b/pkg/analyzer/lib/src/dart/element/replacement_visitor.dart
index d636bf2..c90174a 100644
--- a/pkg/analyzer/lib/src/dart/element/replacement_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/element/replacement_visitor.dart
@@ -13,12 +13,17 @@
 import 'package:analyzer/src/dart/element/type_schema.dart';
 import 'package:analyzer/src/dart/element/type_visitor.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:analyzer/src/summary2/function_type_builder.dart';
+import 'package:analyzer/src/summary2/named_type_builder.dart';
 import 'package:meta/meta.dart';
 
 /// Helper visitor that clones a type if a nested type is replaced, and
 /// otherwise returns `null`.
 class ReplacementVisitor
-    implements TypeVisitor<DartType>, InferenceTypeVisitor<DartType> {
+    implements
+        TypeVisitor<DartType>,
+        InferenceTypeVisitor<DartType>,
+        LinkingTypeVisitor<DartType> {
   const ReplacementVisitor();
 
   void changeVariance() {}
@@ -47,6 +52,27 @@
     );
   }
 
+  DartType createFunctionTypeBuilder({
+    @required FunctionTypeBuilder type,
+    @required List<TypeParameterElement> newTypeParameters,
+    @required List<ParameterElement> newParameters,
+    @required DartType newReturnType,
+    @required NullabilitySuffix newNullability,
+  }) {
+    if (newNullability == null &&
+        newReturnType == null &&
+        newParameters == null) {
+      return null;
+    }
+
+    return FunctionTypeBuilder(
+      newTypeParameters ?? type.typeFormals,
+      newParameters ?? type.parameters,
+      newReturnType ?? type.returnType,
+      newNullability ?? type.nullabilitySuffix,
+    );
+  }
+
   DartType createInterfaceType({
     @required InterfaceType type,
     @required List<DartType> newTypeArguments,
@@ -63,6 +89,23 @@
     );
   }
 
+  NamedTypeBuilder createNamedTypeBuilder({
+    @required NamedTypeBuilder type,
+    @required List<DartType> newTypeArguments,
+    @required NullabilitySuffix newNullability,
+  }) {
+    if (newTypeArguments == null && newNullability == null) {
+      return null;
+    }
+
+    return NamedTypeBuilder(
+      type.typeSystem,
+      type.element,
+      newTypeArguments ?? type.arguments,
+      newNullability ?? type.nullabilitySuffix,
+    );
+  }
+
   DartType createNeverType({
     @required NeverTypeImpl type,
     @required NullabilitySuffix newNullability,
@@ -207,17 +250,99 @@
   }
 
   @override
+  DartType visitFunctionTypeBuilder(FunctionTypeBuilder node) {
+    var newNullability = visitNullability(node);
+
+    List<TypeParameterElement> newTypeParameters;
+    for (var i = 0; i < node.typeFormals.length; i++) {
+      var typeParameter = node.typeFormals[i];
+      var bound = typeParameter.bound;
+      if (bound != null) {
+        var newBound = bound.accept(this);
+        if (newBound != null) {
+          newTypeParameters ??= node.typeFormals.toList(growable: false);
+          newTypeParameters[i] = TypeParameterElementImpl.synthetic(
+            typeParameter.name,
+          )..bound = newBound;
+        }
+      }
+    }
+
+    Substitution substitution;
+    if (newTypeParameters != null) {
+      var map = <TypeParameterElement, DartType>{};
+      for (var i = 0; i < newTypeParameters.length; ++i) {
+        var typeParameter = node.typeFormals[i];
+        var newTypeParameter = newTypeParameters[i];
+        map[typeParameter] = newTypeParameter.instantiate(
+          nullabilitySuffix: NullabilitySuffix.none,
+        );
+      }
+
+      substitution = Substitution.fromMap(map);
+
+      for (var i = 0; i < newTypeParameters.length; i++) {
+        var newTypeParameter = newTypeParameters[i];
+        var bound = newTypeParameter.bound;
+        if (bound != null) {
+          var newBound = substitution.substituteType(bound);
+          (newTypeParameter as TypeParameterElementImpl).bound = newBound;
+        }
+      }
+    }
+
+    DartType visitType(DartType type) {
+      if (type == null) return null;
+      var result = type.accept(this);
+      if (substitution != null) {
+        result = substitution.substituteType(result ?? type);
+      }
+      return result;
+    }
+
+    var newReturnType = visitType(node.returnType);
+
+    changeVariance();
+
+    List<ParameterElement> newParameters;
+    for (var i = 0; i < node.parameters.length; i++) {
+      var parameter = node.parameters[i];
+
+      var type = parameter.type;
+      var newType = visitType(type);
+
+      // ignore: deprecated_member_use_from_same_package
+      var kind = parameter.parameterKind;
+      var newKind = visitParameterKind(kind);
+
+      if (newType != null || newKind != null) {
+        newParameters ??= node.parameters.toList(growable: false);
+        newParameters[i] = parameter.copyWith(
+          type: newType,
+          kind: newKind,
+        );
+      }
+    }
+
+    changeVariance();
+
+    return createFunctionTypeBuilder(
+      type: node,
+      newTypeParameters: newTypeParameters,
+      newParameters: newParameters,
+      newReturnType: newReturnType,
+      newNullability: newNullability,
+    );
+  }
+
+  @override
   DartType visitInterfaceType(InterfaceType type) {
     var newNullability = visitNullability(type);
 
-    List<DartType> newTypeArguments;
-    for (var i = 0; i < type.typeArguments.length; i++) {
-      var substitution = type.typeArguments[i].accept(this);
-      if (substitution != null) {
-        newTypeArguments ??= type.typeArguments.toList(growable: false);
-        newTypeArguments[i] = substitution;
-      }
-    }
+    var newTypeArguments = _typeArguments(
+      type.element.typeParameters,
+      type.typeArguments,
+    );
 
     return createInterfaceType(
       type: type,
@@ -227,6 +352,26 @@
   }
 
   @override
+  DartType visitNamedTypeBuilder(NamedTypeBuilder type) {
+    var newNullability = visitNullability(type);
+
+    var parameters = const <TypeParameterElement>[];
+    var element = type.element;
+    if (element is ClassElement) {
+      parameters = element.typeParameters;
+    } else if (element is FunctionTypeAliasElement) {
+      parameters = element.typeParameters;
+    }
+
+    var newArguments = _typeArguments(parameters, type.arguments);
+    return createNamedTypeBuilder(
+      type: type,
+      newTypeArguments: newArguments,
+      newNullability: newNullability,
+    );
+  }
+
+  @override
   DartType visitNeverType(NeverType type) {
     var newNullability = visitNullability(type);
 
@@ -244,6 +389,11 @@
     return null;
   }
 
+  DartType visitTypeArgument(
+      TypeParameterElement parameter, DartType argument) {
+    return argument.accept(this);
+  }
+
   @override
   DartType visitTypeParameterType(TypeParameterType type) {
     var newNullability = visitNullability(type);
@@ -273,4 +423,28 @@
   DartType visitVoidType(VoidType type) {
     return null;
   }
+
+  List<DartType> _typeArguments(
+    List<TypeParameterElement> parameters,
+    List<DartType> arguments,
+  ) {
+    if (arguments == null) {
+      return null;
+    }
+
+    if (arguments.length != parameters.length) {
+      return null;
+    }
+
+    List<DartType> newArguments;
+    for (var i = 0; i < arguments.length; i++) {
+      var substitution = visitTypeArgument(parameters[i], arguments[i]);
+      if (substitution != null) {
+        newArguments ??= arguments.toList(growable: false);
+        newArguments[i] = substitution;
+      }
+    }
+
+    return newArguments;
+  }
 }
diff --git a/pkg/analyzer/lib/src/summary2/default_types_builder.dart b/pkg/analyzer/lib/src/summary2/default_types_builder.dart
index ce0b852..d06952b 100644
--- a/pkg/analyzer/lib/src/summary2/default_types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/default_types_builder.dart
@@ -6,13 +6,15 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/replacement_visitor.dart';
 import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/dart/element/type_algebra.dart';
+import 'package:analyzer/src/dart/resolver/variance.dart';
 import 'package:analyzer/src/summary2/function_type_builder.dart';
 import 'package:analyzer/src/summary2/lazy_ast.dart';
 import 'package:analyzer/src/summary2/named_type_builder.dart';
 import 'package:analyzer/src/summary2/type_builder.dart';
 import 'package:analyzer/src/util/graph.dart';
+import 'package:meta/meta.dart';
 
 class DefaultTypesBuilder {
   void build(List<AstNode> nodes) {
@@ -152,12 +154,14 @@
     if (parameterList == null) return;
 
     var typeProvider = declarationElement.library.typeProvider;
-    var dynamicType = typeProvider.dynamicType;
-    var nullType = typeProvider.nullType;
+    var dynamicType = DynamicTypeImpl.instance;
+    var bottomType = declarationElement.library.isNonNullableByDefault
+        ? NeverTypeImpl.instance
+        : typeProvider.nullType;
 
     var nodes = parameterList.typeParameters;
     var length = nodes.length;
-    var elements = List<TypeParameterElement>(length);
+    var elements = List<TypeParameterElementImpl>(length);
     var bounds = List<DartType>(length);
     for (int i = 0; i < length; i++) {
       var node = nodes[i];
@@ -173,15 +177,17 @@
       for (var i in component) {
         var element = elements[i];
         dynamicSubstitution[element] = dynamicType;
-        nullSubstitution[element] = nullType;
+        nullSubstitution[element] = bottomType;
       }
 
-      var substitution = Substitution.fromUpperAndLowerBounds(
-        dynamicSubstitution,
-        nullSubstitution,
-      );
       for (var i in component) {
-        bounds[i] = substitution.substituteType(bounds[i]);
+        var variable = elements[i];
+        var visitor = _UpperLowerReplacementVisitor(
+          upper: dynamicSubstitution,
+          lower: nullSubstitution,
+          variance: variable.variance,
+        );
+        bounds[i] = visitor.run(bounds[i]);
       }
     }
 
@@ -190,14 +196,16 @@
       var nullSubstitution = <TypeParameterElement, DartType>{};
       var element = elements[i];
       thisSubstitution[element] = bounds[i];
-      nullSubstitution[element] = nullType;
+      nullSubstitution[element] = bottomType;
 
-      var substitution = Substitution.fromUpperAndLowerBounds(
-        thisSubstitution,
-        nullSubstitution,
-      );
       for (var j = 0; j < length; j++) {
-        bounds[j] = substitution.substituteType(bounds[j]);
+        var variable = elements[j];
+        var visitor = _UpperLowerReplacementVisitor(
+          upper: thisSubstitution,
+          lower: nullSubstitution,
+          variance: variable.variance,
+        );
+        bounds[j] = visitor.run(bounds[j]);
       }
     }
 
@@ -355,3 +363,55 @@
     }
   }
 }
+
+class _UpperLowerReplacementVisitor extends ReplacementVisitor {
+  final Map<TypeParameterElement, DartType> _upper;
+  final Map<TypeParameterElement, DartType> _lower;
+  Variance _variance;
+
+  _UpperLowerReplacementVisitor({
+    @required Map<TypeParameterElement, DartType> upper,
+    @required Map<TypeParameterElement, DartType> lower,
+    @required Variance variance,
+  })  : _upper = upper,
+        _lower = lower,
+        _variance = variance;
+
+  @override
+  void changeVariance() {
+    if (_variance == Variance.covariant) {
+      _variance = Variance.contravariant;
+    } else if (_variance == Variance.contravariant) {
+      _variance = Variance.covariant;
+    }
+  }
+
+  DartType run(DartType type) {
+    return type.accept(this) ?? type;
+  }
+
+  @override
+  DartType visitTypeArgument(
+    TypeParameterElement parameter,
+    DartType argument,
+  ) {
+    var savedVariance = _variance;
+    try {
+      _variance = _variance.combine(
+        (parameter as TypeParameterElementImpl).variance,
+      );
+      return super.visitTypeArgument(parameter, argument);
+    } finally {
+      _variance = savedVariance;
+    }
+  }
+
+  @override
+  DartType visitTypeParameterType(TypeParameterType type) {
+    if (_variance == Variance.contravariant) {
+      return _lower[type.element];
+    } else {
+      return _upper[type.element];
+    }
+  }
+}
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 77e7e0e..863692f 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -1766,6 +1766,135 @@
         withTypeParameterVariance: true);
   }
 
+  test_class_typeParameters_defaultType_functionTypeAlias_contravariant_legacy() async {
+    var library = await checkLibrary(r'''
+typedef F<X> = void Function(X);
+
+class A<X extends F<X>> {}
+''');
+    checkElementText(
+        library,
+        r'''
+typedef F<contravariant X> = void Function(X );
+notSimplyBounded class A<covariant X extends void Function(X) = void Function(Null)> {
+}
+''',
+        withTypeParameterVariance: true);
+  }
+
+  test_class_typeParameters_defaultType_functionTypeAlias_contravariant_nullSafe() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary(r'''
+typedef F<X> = void Function(X);
+
+class A<X extends F<X>> {}
+''');
+    checkElementText(
+        library,
+        r'''
+typedef F<contravariant X> = void Function(X );
+notSimplyBounded class A<covariant X extends void Function(X) = void Function(Never)> {
+}
+''',
+        withTypeParameterVariance: true);
+  }
+
+  test_class_typeParameters_defaultType_functionTypeAlias_invariant_legacy() async {
+    var library = await checkLibrary(r'''
+typedef F<X> = X Function(X);
+
+class A<X extends F<X>> {}
+''');
+    checkElementText(
+        library,
+        r'''
+typedef F<invariant X> = X Function(X );
+notSimplyBounded class A<covariant X extends X Function(X) = dynamic Function(dynamic)> {
+}
+''',
+        withTypeParameterVariance: true);
+  }
+
+  test_class_typeParameters_defaultType_functionTypeAlias_invariant_nullSafe() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary(r'''
+typedef F<X> = X Function(X);
+
+class A<X extends F<X>> {}
+''');
+    checkElementText(
+        library,
+        r'''
+typedef F<invariant X> = X Function(X );
+notSimplyBounded class A<covariant X extends X Function(X) = dynamic Function(dynamic)> {
+}
+''',
+        withTypeParameterVariance: true);
+  }
+
+  test_class_typeParameters_defaultType_genericFunctionType_both_legacy() async {
+    var library = await checkLibrary(r'''
+class A<X extends X Function(X)> {}
+''');
+    checkElementText(library, r'''
+notSimplyBounded class A<X extends X Function(X) = dynamic Function(Null)> {
+}
+''');
+  }
+
+  test_class_typeParameters_defaultType_genericFunctionType_both_nullSafe() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary(r'''
+class A<X extends X Function(X)> {}
+''');
+    checkElementText(library, r'''
+notSimplyBounded class A<X extends X Function(X) = dynamic Function(Never)> {
+}
+''');
+  }
+
+  test_class_typeParameters_defaultType_genericFunctionType_contravariant_legacy() async {
+    var library = await checkLibrary(r'''
+class A<X extends void Function(X)> {}
+''');
+    checkElementText(library, r'''
+notSimplyBounded class A<X extends void Function(X) = void Function(Null)> {
+}
+''');
+  }
+
+  test_class_typeParameters_defaultType_genericFunctionType_contravariant_nullSafe() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary(r'''
+class A<X extends void Function(X)> {}
+''');
+    checkElementText(library, r'''
+notSimplyBounded class A<X extends void Function(X) = void Function(Never)> {
+}
+''');
+  }
+
+  test_class_typeParameters_defaultType_genericFunctionType_covariant_legacy() async {
+    var library = await checkLibrary(r'''
+class A<X extends X Function()> {}
+''');
+    checkElementText(library, r'''
+notSimplyBounded class A<X extends X Function() = dynamic Function()> {
+}
+''');
+  }
+
+  test_class_typeParameters_defaultType_genericFunctionType_covariant_nullSafe() async {
+    featureSet = enableNnbd;
+    var library = await checkLibrary(r'''
+class A<X extends X Function()> {}
+''');
+    checkElementText(library, r'''
+notSimplyBounded class A<X extends X Function() = dynamic Function()> {
+}
+''');
+  }
+
   test_classes() async {
     var library = await checkLibrary('class C {} class D {}');
     checkElementText(library, r'''
@@ -11463,14 +11592,14 @@
   test_typedef_type_parameters_f_bound_complex() async {
     var library = await checkLibrary('typedef U F<T extends List<U>, U>(T t);');
     checkElementText(library, r'''
-notSimplyBounded typedef F<T extends List<U> = List<dynamic>, U> = U Function(T t);
+notSimplyBounded typedef F<T extends List<U> = List<Null>, U> = U Function(T t);
 ''');
   }
 
   test_typedef_type_parameters_f_bound_simple() async {
     var library = await checkLibrary('typedef U F<T extends U, U>(T t);');
     checkElementText(library, r'''
-notSimplyBounded typedef F<T extends U, U> = U Function(T t);
+notSimplyBounded typedef F<T extends U = Null, U> = U Function(T t);
 ''');
   }
 
@@ -11478,7 +11607,7 @@
     var library =
         await checkLibrary('typedef F<T extends U, U> = U Function(T t);');
     checkElementText(library, r'''
-notSimplyBounded typedef F<T extends U, U> = U Function(T t);
+notSimplyBounded typedef F<T extends U = Null, U> = U Function(T t);
 ''');
   }