[fasta] Fix override checks for generic methods

Fixes #33971.

Bug: http://dartbug.com/33971
Change-Id: I66ee51ccfa0b8109602593f04961b58bf589d0be
Reviewed-on: https://dart-review.googlesource.com/66560
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
index 854ac86..38db74e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
@@ -667,7 +667,12 @@
           ]);
     } else if (library.loader.target.backendTarget.strongMode &&
         declaredFunction?.typeParameters != null) {
-      var substitution = <TypeParameter, DartType>{};
+      var substitutionMap = <TypeParameter, DartType>{};
+      for (int i = 0; i < declaredFunction.typeParameters.length; ++i) {
+        substitutionMap[interfaceFunction.typeParameters[i]] =
+            new TypeParameterType(declaredFunction.typeParameters[i]);
+      }
+      Substitution substitution = Substitution.fromMap(substitutionMap);
       for (int i = 0; i < declaredFunction.typeParameters.length; ++i) {
         TypeParameter declaredParameter = declaredFunction.typeParameters[i];
         TypeParameter interfaceParameter = interfaceFunction.typeParameters[i];
@@ -679,7 +684,7 @@
             interfaceBound =
                 interfaceSubstitution.substituteType(interfaceBound);
           }
-          if (declaredBound != interfaceBound) {
+          if (declaredBound != substitution.substituteType(interfaceBound)) {
             addProblem(
                 templateOverrideTypeVariablesMismatch.withArguments(
                     "$name::${declaredMember.name.name}",
@@ -695,13 +700,10 @@
                 ]);
           }
         }
-        substitution[interfaceParameter] =
-            new TypeParameterType(declaredParameter);
       }
-      var newSubstitution = Substitution.fromMap(substitution);
       interfaceSubstitution = interfaceSubstitution == null
-          ? newSubstitution
-          : Substitution.combine(interfaceSubstitution, newSubstitution);
+          ? substitution
+          : Substitution.combine(interfaceSubstitution, substitution);
     }
     return interfaceSubstitution;
   }
diff --git a/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart
new file mode 100644
index 0000000..3058fbf
--- /dev/null
+++ b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2018, 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.
+
+// This test checks that an override of a generic method is allowed in case of
+// correctly defined f-bounded type variables.
+
+class Foo<T extends Foo<T>> {}
+
+abstract class Bar {
+  void fisk<S extends Foo<S>>();
+}
+
+class Hest implements Bar {
+  @override
+  void fisk<U extends Foo<U>>() {}
+}
+
+void main() {}
diff --git a/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.direct.expect b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.direct.expect
new file mode 100644
index 0000000..b01aeb7
--- /dev/null
+++ b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.direct.expect
@@ -0,0 +1,23 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Foo<T extends self::Foo<self::Foo::T> = dynamic> extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+abstract class Bar extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  abstract method fisk<S extends self::Foo<self::Bar::fisk::S> = dynamic>() → void;
+}
+class Hest extends core::Object implements self::Bar {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @core::override
+  method fisk<U extends self::Foo<self::Hest::fisk::U> = dynamic>() → void {}
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.direct.transformed.expect b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.direct.transformed.expect
new file mode 100644
index 0000000..b01aeb7
--- /dev/null
+++ b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.direct.transformed.expect
@@ -0,0 +1,23 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Foo<T extends self::Foo<self::Foo::T> = dynamic> extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+abstract class Bar extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  abstract method fisk<S extends self::Foo<self::Bar::fisk::S> = dynamic>() → void;
+}
+class Hest extends core::Object implements self::Bar {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @core::override
+  method fisk<U extends self::Foo<self::Hest::fisk::U> = dynamic>() → void {}
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.outline.expect b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.outline.expect
new file mode 100644
index 0000000..f6befa0
--- /dev/null
+++ b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.outline.expect
@@ -0,0 +1,21 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Foo<T extends self::Foo<self::Foo::T> = dynamic> extends core::Object {
+  synthetic constructor •() → void
+    ;
+}
+abstract class Bar extends core::Object {
+  synthetic constructor •() → void
+    ;
+  abstract method fisk<S extends self::Foo<self::Bar::fisk::S> = dynamic>() → void;
+}
+class Hest extends core::Object implements self::Bar {
+  synthetic constructor •() → void
+    ;
+  method fisk<U extends self::Foo<self::Hest::fisk::U> = dynamic>() → void
+    ;
+}
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.strong.expect b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.strong.expect
new file mode 100644
index 0000000..d4dc158
--- /dev/null
+++ b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.strong.expect
@@ -0,0 +1,23 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Foo<T extends self::Foo<self::Foo::T> = self::Foo<dynamic>> extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+abstract class Bar extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  abstract method fisk<S extends self::Foo<self::Bar::fisk::S> = self::Foo<dynamic>>() → void;
+}
+class Hest extends core::Object implements self::Bar {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @core::override
+  method fisk<U extends self::Foo<self::Hest::fisk::U> = self::Foo<dynamic>>() → void {}
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.strong.transformed.expect b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.strong.transformed.expect
new file mode 100644
index 0000000..d4dc158
--- /dev/null
+++ b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.strong.transformed.expect
@@ -0,0 +1,23 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Foo<T extends self::Foo<self::Foo::T> = self::Foo<dynamic>> extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+abstract class Bar extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  abstract method fisk<S extends self::Foo<self::Bar::fisk::S> = self::Foo<dynamic>>() → void;
+}
+class Hest extends core::Object implements self::Bar {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @core::override
+  method fisk<U extends self::Foo<self::Hest::fisk::U> = self::Foo<dynamic>>() → void {}
+}
+static method main() → void {}