Compute substitution map on kernel objects instead of builders

Fixes https://github.com/dart-lang/sdk/issues/34856

Change-Id: I150a390ea19e684e7951c1354c09fd40845e4328
Reviewed-on: https://dart-review.googlesource.com/c/81006
Reviewed-by: Kevin Millikin <kmillikin@google.com>
Commit-Queue: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index 706a85d..6769e5c 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -4935,30 +4935,6 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<Message Function(String name)>
-    templateInternalProblemSuperclassNotFound =
-    const Template<Message Function(String name)>(
-        messageTemplate: r"""Superclass not found '#name'.""",
-        withArguments: _withArgumentsInternalProblemSuperclassNotFound);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String name)>
-    codeInternalProblemSuperclassNotFound =
-    const Code<Message Function(String name)>(
-        "InternalProblemSuperclassNotFound",
-        templateInternalProblemSuperclassNotFound,
-        severity: Severity.internalProblem);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsInternalProblemSuperclassNotFound(String name) {
-  if (name.isEmpty) throw 'No name provided';
-  name = demangleMixinApplicationName(name);
-  return new Message(codeInternalProblemSuperclassNotFound,
-      message: """Superclass not found '${name}'.""",
-      arguments: {'name': name});
-}
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String string, String string2)>
     templateInternalProblemUnexpected =
     const Template<Message Function(String string, String string2)>(
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 b913847..56bc3b2 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
@@ -41,7 +41,7 @@
 
 import 'package:kernel/core_types.dart' show CoreTypes;
 
-import 'package:kernel/type_algebra.dart' show Substitution;
+import 'package:kernel/type_algebra.dart' show Substitution, substitute;
 
 import 'package:kernel/type_algebra.dart' as type_algebra
     show getSubstitutionMap;
@@ -73,7 +73,6 @@
         templateIncorrectTypeArgumentInSupertype,
         templateIncorrectTypeArgumentInSupertypeInferred,
         templateInterfaceCheckContext,
-        templateInternalProblemSuperclassNotFound,
         templateMissingImplementationCause,
         templateMissingImplementationNotAbstract,
         templateMixinApplicationIncompatibleSupertype,
@@ -96,8 +95,7 @@
 
 import '../names.dart' show noSuchMethodName;
 
-import '../problems.dart'
-    show internalProblem, unexpected, unhandled, unimplemented;
+import '../problems.dart' show unexpected, unhandled, unimplemented;
 
 import '../type_inference/type_schema.dart' show UnknownType;
 
@@ -116,11 +114,8 @@
         LibraryBuilder,
         MemberBuilder,
         MetadataBuilder,
-        MixinApplicationBuilder,
-        NamedTypeBuilder,
         ProcedureBuilder,
         Scope,
-        TypeBuilder,
         TypeVariableBuilder;
 
 import 'redirecting_factory_body.dart'
@@ -1741,66 +1736,30 @@
   ///
   ///     [[BeatBox]].getSubstitutionMap([[Box]]) -> {[[Box::T]]: Beat]]}.
   ///
-  /// This method returns null if the map is empty, and it's an error if
-  /// [superclass] isn't a superclass.
-  Map<TypeParameter, DartType> getSubstitutionMap(ClassBuilder superclass,
-      Uri fileUri, int charOffset, TypeBuilder dynamicType) {
-    TypeBuilder supertype = this.supertype;
-    Map<TypeVariableBuilder, TypeBuilder> substitutionMap;
-    List arguments;
-    List variables;
-    Declaration declaration;
+  /// It's an error if [superclass] isn't a superclass.
+  Map<TypeParameter, DartType> getSubstitutionMap(Class superclass) {
+    Supertype supertype = target.supertype;
+    Map<TypeParameter, DartType> substitutionMap = <TypeParameter, DartType>{};
+    List<DartType> arguments;
+    List<TypeParameter> variables;
+    Class classNode;
 
-    /// If [application] is mixing in [superclass] directly or via other named
-    /// mixin applications, return it.
-    NamedTypeBuilder findSuperclass(MixinApplicationBuilder application) {
-      for (TypeBuilder t in application.mixins) {
-        if (t is NamedTypeBuilder) {
-          if (t.declaration == superclass) return t;
-        } else if (t is MixinApplicationBuilder) {
-          NamedTypeBuilder s = findSuperclass(t);
-          if (s != null) return s;
-        }
-      }
-      return null;
-    }
-
-    void handleNamedTypeBuilder(NamedTypeBuilder t) {
-      declaration = t.declaration;
-      arguments = t.arguments ?? const [];
-      if (declaration is ClassBuilder) {
-        ClassBuilder cls = declaration;
-        variables = cls.typeVariables;
-        supertype = cls.supertype;
-      }
-    }
-
-    while (declaration != superclass) {
-      variables = null;
-      if (supertype is NamedTypeBuilder) {
-        handleNamedTypeBuilder(supertype);
-      } else if (supertype is MixinApplicationBuilder) {
-        MixinApplicationBuilder t = supertype;
-        NamedTypeBuilder s = findSuperclass(t);
-        if (s != null) {
-          handleNamedTypeBuilder(s);
-        }
-        supertype = t.supertype;
-      } else {
-        internalProblem(
-            templateInternalProblemSuperclassNotFound
-                .withArguments(superclass.fullNameForErrors),
-            charOffset,
-            fileUri);
-      }
-      if (variables != null) {
-        Map<TypeVariableBuilder, TypeBuilder> directSubstitutionMap =
-            <TypeVariableBuilder, TypeBuilder>{};
+    while (classNode != superclass) {
+      classNode = supertype.classNode;
+      arguments = supertype.typeArguments;
+      variables = classNode.typeParameters;
+      supertype = classNode.supertype;
+      if (variables.isNotEmpty) {
+        Map<TypeParameter, DartType> directSubstitutionMap =
+            <TypeParameter, DartType>{};
         for (int i = 0; i < variables.length; i++) {
-          TypeBuilder argument =
-              i < arguments.length ? arguments[i] : dynamicType;
+          DartType argument =
+              i < arguments.length ? arguments[i] : const DynamicType();
           if (substitutionMap != null) {
-            argument = argument.subst(substitutionMap);
+            // TODO(ahe): Investigate if requiring the caller to use
+            // `substituteDeep` from `package:kernel/type_algebra.dart` instead
+            // of `substitute` is faster. If so, we can simply this code.
+            argument = substitute(argument, substitutionMap);
           }
           directSubstitutionMap[variables[i]] = argument;
         }
@@ -1808,13 +1767,6 @@
       }
     }
 
-    if (substitutionMap == null) return const <TypeParameter, DartType>{};
-
-    Map<TypeParameter, DartType> result = <TypeParameter, DartType>{};
-    substitutionMap
-        .forEach((TypeVariableBuilder variable, TypeBuilder argument) {
-      result[variable.target] = argument.build(library);
-    });
-    return result;
+    return substitutionMap;
   }
 }
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index fd807fc..8354ddb 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -481,8 +481,7 @@
         builder.addSyntheticConstructor(makeDefaultConstructor(builder.target));
       } else {
         Map<TypeParameter, DartType> substitutionMap =
-            builder.getSubstitutionMap(
-                supertype, builder.fileUri, builder.charOffset, dynamicType);
+            builder.getSubstitutionMap(supertype.target);
         for (Constructor constructor in supertype.cls.constructors) {
           builder.addSyntheticConstructor(makeMixinApplicationConstructor(
               builder.target, builder.cls.mixin, constructor, substitutionMap));
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 5d3e0cf..60bb455 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -1576,10 +1576,6 @@
   template: "Unsupported operation: '#name'."
   severity: INTERNAL_PROBLEM
 
-InternalProblemSuperclassNotFound:
-  template: "Superclass not found '#name'."
-  severity: INTERNAL_PROBLEM
-
 InternalProblemNotFound:
   template: "Couldn't find '#name'."
   severity: INTERNAL_PROBLEM
diff --git a/pkg/front_end/test/issue_34856_test.dart b/pkg/front_end/test/issue_34856_test.dart
new file mode 100644
index 0000000..c3f6693
--- /dev/null
+++ b/pkg/front_end/test/issue_34856_test.dart
@@ -0,0 +1,108 @@
+// 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.
+
+import 'dart:io' show File;
+
+import 'package:async_helper/async_helper.dart' show asyncTest;
+
+import 'package:front_end/src/api_prototype/compiler_options.dart'
+    show CompilerOptions;
+
+import 'package:front_end/src/api_prototype/kernel_generator.dart'
+    show kernelForComponent;
+
+import 'package:front_end/src/api_prototype/memory_file_system.dart'
+    show MemoryFileSystem;
+
+import 'package:front_end/src/base/processed_options.dart'
+    show ProcessedOptions;
+
+import 'package:front_end/src/compute_platform_binaries_location.dart'
+    show computePlatformBinariesLocation;
+
+import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
+
+import 'package:front_end/src/fasta/kernel/utils.dart' show serializeComponent;
+
+import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyComponent;
+
+import 'package:kernel/ast.dart' show Component;
+
+const Map<String, String> files = const <String, String>{
+  "repro.dart": """
+
+import 'lib.dart';
+
+abstract class M<M_K, M_V> implements Map<M_K, Set<M_V>> {}
+
+abstract class C<C_K, C_V> extends UnmodifiableMapView<C_K, Set<C_V>>
+    with M<C_K, C_V> {
+  C._() : super(null);
+}
+""",
+  "lib.dart": """abstract class MapView<K, V> {
+  const MapView(Map<K, V> map);
+}
+
+abstract class _UnmodifiableMapMixin<K, V> {}
+
+abstract class UnmodifiableMapView<K, V> extends MapView<K, V>
+    with _UnmodifiableMapMixin<K, V> {
+  UnmodifiableMapView(Map<K, V> map) : super(map);
+}""",
+};
+
+Future<void> test() async {
+  final String platformBaseName = "vm_platform_strong.dill";
+  final Uri base = Uri.parse("org-dartlang-test:///");
+  final Uri platformDill = base.resolve(platformBaseName);
+  final List<int> platformDillBytes = await new File.fromUri(
+          computePlatformBinariesLocation(forceBuildDir: true)
+              .resolve(platformBaseName))
+      .readAsBytes();
+  MemoryFileSystem fs = new MemoryFileSystem(base);
+  fs.entityForUri(platformDill).writeAsBytesSync(platformDillBytes);
+  fs
+      .entityForUri(base.resolve("lib.dart"))
+      .writeAsStringSync(files["lib.dart"]);
+  CompilerOptions options = new CompilerOptions()
+    ..fileSystem = fs
+    ..sdkSummary = platformDill;
+
+  Component component =
+      await kernelForComponent(<Uri>[base.resolve("lib.dart")], options);
+
+  fs = new MemoryFileSystem(base);
+  fs.entityForUri(platformDill).writeAsBytesSync(platformDillBytes);
+  fs
+      .entityForUri(base.resolve("lib.dart.dill"))
+      .writeAsBytesSync(serializeComponent(component));
+  fs
+      .entityForUri(base.resolve("repro.dart"))
+      .writeAsStringSync(files["repro.dart"]);
+
+  options = new CompilerOptions()
+    ..fileSystem = fs
+    ..linkedDependencies = <Uri>[base.resolve("lib.dart.dill")]
+    ..sdkSummary = platformDill;
+
+  List<Uri> inputs = <Uri>[base.resolve("repro.dart")];
+
+  component = await kernelForComponent(inputs, options);
+
+  List<Object> errors = await CompilerContext.runWithOptions(
+      new ProcessedOptions(options: options, inputs: inputs),
+      (_) => new Future<List<Object>>.value(
+          verifyComponent(component, skipPlatform: true)));
+
+  serializeComponent(component);
+
+  if (errors.isNotEmpty) {
+    throw "Verification failed";
+  }
+}
+
+main() {
+  asyncTest(test);
+}