Compute supertypes

Change-Id: I85fd7894b026180b1bc418602c0f09fcfea0a543
Reviewed-on: https://dart-review.googlesource.com/c/89543
Commit-Queue: Peter von der Ahé <ahe@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
index af41c71..7e17f6d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
@@ -288,11 +288,22 @@
     /// superclasses.
     List<Declaration> interfaceSetters;
 
+    List<KernelTypeBuilder> superclasses;
+
+    List<KernelTypeBuilder> interfaces;
+
     if (supernode == null) {
       // This should be Object.
       classMembers = localMembers;
       classSetters = localSetters;
+      superclasses = new List<KernelTypeBuilder>(0);
+      interfaces = new List<KernelTypeBuilder>(0);
     } else {
+      superclasses =
+          new List<KernelTypeBuilder>(supernode.superclasses.length + 1);
+      superclasses.setRange(0, superclasses.length - 1, supernode.superclasses);
+      superclasses[superclasses.length - 1] = cls.supertype;
+
       classMembers = merge(
           cls, localMembers, supernode.classMembers, MergeKind.superclass);
       classSetters = merge(
@@ -307,21 +318,47 @@
       // only need to check the local setters.
       merge(cls, localSetters, classMembers, MergeKind.accessors);
 
-      List<KernelTypeBuilder> interfaces = cls.interfaces;
+      List<KernelTypeBuilder> directInterfaces = cls.interfaces;
       if (cls.isMixinApplication) {
-        if (interfaces == null) {
-          interfaces = <KernelTypeBuilder>[cls.mixedInType];
+        if (directInterfaces == null) {
+          directInterfaces = <KernelTypeBuilder>[cls.mixedInType];
         } else {
-          interfaces = <KernelTypeBuilder>[cls.mixedInType]..addAll(interfaces);
+          directInterfaces = <KernelTypeBuilder>[cls.mixedInType]
+            ..addAll(directInterfaces);
         }
       }
-      if (interfaces != null) {
-        MergeResult result = mergeInterfaces(cls, supernode, interfaces);
+      if (directInterfaces != null) {
+        MergeResult result = mergeInterfaces(cls, supernode, directInterfaces);
         interfaceMembers = result.mergedMembers;
         interfaceSetters = result.mergedSetters;
+        interfaces = <KernelTypeBuilder>[];
+        if (supernode.interfaces != null) {
+          List<KernelTypeBuilder> types = supernode.interfaces;
+          for (int i = 0; i < types.length; i++) {
+            addInterface(interfaces, superclasses, types[i]);
+          }
+        }
+        for (int i = 0; i < directInterfaces.length; i++) {
+          addInterface(interfaces, superclasses, directInterfaces[i]);
+          ClassHierarchyNode interfaceNode = getNode(directInterfaces[i]);
+          if (interfaceNode != null) {
+            List<KernelTypeBuilder> types = interfaceNode.superclasses;
+            for (int i = 0; i < types.length; i++) {
+              addInterface(interfaces, superclasses, types[i]);
+            }
+
+            if (interfaceNode.interfaces != null) {
+              List<KernelTypeBuilder> types = interfaceNode.interfaces;
+              for (int i = 0; i < types.length; i++) {
+                addInterface(interfaces, superclasses, types[i]);
+              }
+            }
+          }
+        }
       } else {
         interfaceMembers = supernode.interfaceMembers;
         interfaceSetters = supernode.interfaceSetters;
+        interfaces = supernode.interfaces;
       }
       if (interfaceMembers != null) {
         interfaceMembers =
@@ -340,8 +377,15 @@
         merge(cls, classMembers, interfaceSetters, MergeKind.accessors);
       }
     }
-    nodes[cls] = new ClassHierarchyNode(cls, scope, classMembers, classSetters,
-        interfaceMembers, interfaceSetters);
+    nodes[cls] = new ClassHierarchyNode(
+      cls,
+      classMembers,
+      classSetters,
+      interfaceMembers,
+      interfaceSetters,
+      superclasses,
+      interfaces,
+    );
 
     if (abstractMembers != null && !cls.isAbstract) {
       if (!hasNoSuchMethod) {
@@ -354,6 +398,29 @@
     abstractMembers = null;
   }
 
+  KernelTypeBuilder addInterface(List<KernelTypeBuilder> interfaces,
+      List<KernelTypeBuilder> superclasses, KernelTypeBuilder type) {
+    ClassHierarchyNode node = getNode(type);
+    if (node == null) return null;
+    int depth = node.depth;
+    int myDepth = superclasses.length;
+    if (depth < myDepth && superclasses[depth].declaration == node.cls) {
+      // This is a potential conflict.
+      return superclasses[depth];
+    } else {
+      for (int i = 0; i < interfaces.length; i++) {
+        // This is a quadratic algorithm, but normally, the number of
+        // interfaces is really small.
+        if (interfaces[i].declaration == type.declaration) {
+          // This is a potential conflict.
+          return interfaces[i];
+        }
+      }
+    }
+    interfaces.add(type);
+    return null;
+  }
+
   MergeResult mergeInterfaces(KernelClassBuilder cls,
       ClassHierarchyNode supernode, List<KernelTypeBuilder> interfaces) {
     List<List<Declaration>> memberLists =
@@ -569,13 +636,6 @@
   /// The class corresponding to this hierarchy node.
   final KernelClassBuilder cls;
 
-  /// The local members of [cls]. For regular classes, this is simply
-  /// `cls.scope`, but for mixin-applications this is the mixed-in type's
-  /// scope. The members are sorted in order of declaration.
-  // TODO(ahe): Do we need to copy the scope from the mixed-in type to remove
-  // static members?
-  final Scope localMembers;
-
   /// All the members of this class including [classMembers] of its
   /// superclasses. The members are sorted by [compareDeclarations].
   final List<Declaration> classMembers;
@@ -593,8 +653,75 @@
   /// Similar to [interfaceMembers] but for setters.
   final List<Declaration> interfaceSetters;
 
-  ClassHierarchyNode(this.cls, this.localMembers, this.classMembers,
-      this.classSetters, this.interfaceMembers, this.interfaceSetters);
+  /// All superclasses of [cls] excluding itself. The classes are sorted by
+  /// depth from the root (Object) in ascending order.
+  final List<KernelTypeBuilder> superclasses;
+
+  /// The list of all classes implemented by [cls] and its supertypes excluding
+  /// any classes from [superclasses].
+  final List<KernelTypeBuilder> interfaces;
+
+  int get depth => superclasses.length;
+
+  ClassHierarchyNode(
+      this.cls,
+      this.classMembers,
+      this.classSetters,
+      this.interfaceMembers,
+      this.interfaceSetters,
+      this.superclasses,
+      this.interfaces);
+
+  String toString([StringBuffer sb]) {
+    sb ??= new StringBuffer();
+    sb
+      ..write(cls.fullNameForErrors)
+      ..writeln(":")
+      ..writeln("  superclasses:");
+    int depth = 0;
+    for (KernelTypeBuilder superclass in superclasses) {
+      sb.write("  " * (depth + 2));
+      if (depth != 0) sb.write("-> ");
+      superclass.printOn(sb);
+      sb.writeln();
+      depth++;
+    }
+    if (interfaces != null) {
+      sb.write("  interfaces:");
+      bool first = true;
+      for (KernelTypeBuilder i in interfaces) {
+        if (!first) sb.write(",");
+        sb.write(" ");
+        i.printOn(sb);
+        first = false;
+      }
+      sb.writeln();
+    }
+    printMembers(classMembers, sb, "classMembers");
+    printMembers(classSetters, sb, "classSetters");
+    if (interfaceMembers != null) {
+      printMembers(interfaceMembers, sb, "interfaceMembers");
+    }
+    if (interfaceSetters != null) {
+      printMembers(interfaceSetters, sb, "interfaceSetters");
+    }
+    return "$sb";
+  }
+
+  void printMembers(
+      List<Declaration> members, StringBuffer sb, String heading) {
+    sb.write("  ");
+    sb.write(heading);
+    sb.writeln(":");
+    for (Declaration member in members) {
+      sb
+        ..write("    ")
+        ..write(member.parent.fullNameForErrors)
+        ..write(".")
+        ..write(member.fullNameForErrors)
+        ..writeln();
+    }
+  }
 }
 
 class MergeResult {
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 f1b78d6..d52394c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -84,6 +84,7 @@
 import 'kernel_builder.dart'
     show
         ClassBuilder,
+        ClassHierarchyBuilder,
         Declaration,
         InvalidTypeBuilder,
         KernelClassBuilder,
@@ -145,6 +146,8 @@
     loader = createLoader();
   }
 
+  void set builderHierarchy(ClassHierarchyBuilder o) {}
+
   SourceLoader<Library> createLoader() =>
       new SourceLoader<Library>(fileSystem, includeComments, this);
 
@@ -254,7 +257,8 @@
       component =
           link(new List<Library>.from(loader.libraries), nameRoot: nameRoot);
       computeCoreTypes();
-      loader.buildClassHierarchy(myClasses, objectClassBuilder);
+      builderHierarchy =
+          loader.buildClassHierarchy(myClasses, objectClassBuilder);
       loader.computeHierarchy();
       loader.performTopLevelInference(myClasses);
       loader.checkSupertypes(myClasses);
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index d951035..8679e5a 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -926,7 +926,7 @@
     ticker.logMs("Checked mixin declaration applications");
   }
 
-  void buildClassHierarchy(
+  ClassHierarchyBuilder buildClassHierarchy(
       List<SourceClassBuilder> sourceClasses, ClassBuilder objectClass) {
     ticker.logMs("Building class hierarchy");
     ClassHierarchyBuilder classHierarchyBuilder =
@@ -935,6 +935,7 @@
       classHierarchyBuilder.add(sourceClasses[i]);
     }
     ticker.logMs("Built class hierarchy");
+    return classHierarchyBuilder;
   }
 
   void createTypeInferenceEngine() {
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index eab5b94..02a8e3a 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -55,6 +55,12 @@
 
 import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget;
 
+import 'package:front_end/src/fasta/kernel/class_hierarchy_builder.dart'
+    show ClassHierarchyNode;
+
+import 'package:front_end/src/fasta/kernel/kernel_builder.dart'
+    show ClassHierarchyBuilder;
+
 import 'package:front_end/src/fasta/kernel/kernel_target.dart'
     show KernelTarget;
 
@@ -173,6 +179,9 @@
     if (kernelTextSerialization) {
       steps.add(const KernelTextSerialization());
     }
+    if (legacyMode && !fullCompile) {
+      steps.add(new MatchHierarchy());
+    }
     if (fullCompile) {
       steps.add(const Transform());
       if (!ignoreExpectations) {
@@ -348,7 +357,7 @@
       UriTranslator uriTranslator = new UriTranslator(
           const TargetLibrariesSpecification('vm'),
           context.uriTranslator.packages);
-      KernelTarget sourceTarget = new KernelTarget(
+      KernelTarget sourceTarget = new KernelTestingTarget(
           StandardFileSystem.instance, false, dillTarget, uriTranslator);
 
       sourceTarget.setEntryPoints(<Uri>[description.uri]);
@@ -438,3 +447,32 @@
         : fail(component, """Unexpected errors:\n$buffer""");
   }
 }
+
+class KernelTestingTarget extends KernelTarget {
+  @override
+  ClassHierarchyBuilder builderHierarchy;
+
+  KernelTestingTarget(StandardFileSystem fileSystem, bool includeComments,
+      DillTarget dillTarget, UriTranslator uriTranslator)
+      : super(fileSystem, includeComments, dillTarget, uriTranslator);
+}
+
+class MatchHierarchy extends Step<Component, Component, FastaContext> {
+  const MatchHierarchy();
+
+  String get name => "check hierarchy";
+
+  Future<Result<Component>> run(
+      Component component, FastaContext context) async {
+    Uri uri =
+        component.uriToSource.keys.firstWhere((uri) => uri?.scheme == "file");
+    KernelTestingTarget target = context.componentToTarget[component];
+    ClassHierarchyBuilder hierarchy = target.builderHierarchy;
+    StringBuffer sb = new StringBuffer();
+    for (ClassHierarchyNode node in hierarchy.nodes.values) {
+      node.toString(sb);
+      sb.writeln();
+    }
+    return context.match<Component>(".hierarchy.expect", "$sb", uri, component);
+  }
+}
diff --git a/pkg/front_end/testcases/complex_class_hierarchy.dart b/pkg/front_end/testcases/complex_class_hierarchy.dart
new file mode 100644
index 0000000..86dba1e
--- /dev/null
+++ b/pkg/front_end/testcases/complex_class_hierarchy.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2019, 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.
+
+main() {}
+
+class A {}
+
+class B extends A {}
+
+class C extends B {}
+
+class D extends C {}
+
+class G<T extends A> {}
+
+class GB extends G<B> {}
+
+class GC extends G<C> {}
+
+class GD extends G<D> {}
+
+class X implements A {}
+
+class Y extends X {}
+
+class Z implements Y {}
+
+class W implements Z {}
+
+class GX implements G<A> {}
+
+class GY extends X implements GB {}
+
+class GZ implements Y, GC {}
+
+class GW implements Z, GD {}
+
+class GU extends GW {}
+
+class GV extends GU implements GW {}
diff --git a/pkg/front_end/testcases/complex_class_hierarchy.dart.hierarchy.expect b/pkg/front_end/testcases/complex_class_hierarchy.dart.hierarchy.expect
new file mode 100644
index 0000000..b3c7b13
--- /dev/null
+++ b/pkg/front_end/testcases/complex_class_hierarchy.dart.hierarchy.expect
@@ -0,0 +1,459 @@
+Object:
+  superclasses:
+  interfaces:
+  classMembers:
+    Object._haveSameRuntimeType
+    Object.toString
+    Object.runtimeType
+    Object._toString
+    Object._simpleInstanceOf
+    Object._hashCodeRnd
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._objectHashCode
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+A:
+  superclasses:
+    Object
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+B:
+  superclasses:
+    Object
+      -> A
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+C:
+  superclasses:
+    Object
+      -> A
+        -> B
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+D:
+  superclasses:
+    Object
+      -> A
+        -> B
+          -> C
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+G:
+  superclasses:
+    Object
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+GB:
+  superclasses:
+    Object
+      -> G<B>
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+GC:
+  superclasses:
+    Object
+      -> G<C>
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+GD:
+  superclasses:
+    Object
+      -> G<D>
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+X:
+  superclasses:
+    Object
+  interfaces: A
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+Y:
+  superclasses:
+    Object
+      -> X
+  interfaces: A
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+Z:
+  superclasses:
+    Object
+  interfaces: Y, X, A
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+W:
+  superclasses:
+    Object
+  interfaces: Z, Y, X, A
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+GX:
+  superclasses:
+    Object
+  interfaces: G<A>
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+GY:
+  superclasses:
+    Object
+      -> X
+  interfaces: A, GB, G<B>
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+GZ:
+  superclasses:
+    Object
+  interfaces: Y, X, A, GC, G<C>
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+GW:
+  superclasses:
+    Object
+  interfaces: Z, Y, X, A, GD, G<D>
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+GU:
+  superclasses:
+    Object
+      -> GW
+  interfaces: Z, Y, X, A, GD, G<D>
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
+
+GV:
+  superclasses:
+    Object
+      -> GW
+        -> GU
+  interfaces: Z, Y, X, A, GD, G<D>
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
diff --git a/pkg/front_end/testcases/complex_class_hierarchy.dart.legacy.expect b/pkg/front_end/testcases/complex_class_hierarchy.dart.legacy.expect
new file mode 100644
index 0000000..faa6b6d
--- /dev/null
+++ b/pkg/front_end/testcases/complex_class_hierarchy.dart.legacy.expect
@@ -0,0 +1,95 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+class C extends self::B {
+  synthetic constructor •() → self::C
+    : super self::B::•()
+    ;
+}
+class D extends self::C {
+  synthetic constructor •() → self::D
+    : super self::C::•()
+    ;
+}
+class G<T extends self::A = dynamic> extends core::Object {
+  synthetic constructor •() → self::G<self::G::T>
+    : super core::Object::•()
+    ;
+}
+class GB extends self::G<self::B> {
+  synthetic constructor •() → self::GB
+    : super self::G::•()
+    ;
+}
+class GC extends self::G<self::C> {
+  synthetic constructor •() → self::GC
+    : super self::G::•()
+    ;
+}
+class GD extends self::G<self::D> {
+  synthetic constructor •() → self::GD
+    : super self::G::•()
+    ;
+}
+class X extends core::Object implements self::A {
+  synthetic constructor •() → self::X
+    : super core::Object::•()
+    ;
+}
+class Y extends self::X {
+  synthetic constructor •() → self::Y
+    : super self::X::•()
+    ;
+}
+class Z extends core::Object implements self::Y {
+  synthetic constructor •() → self::Z
+    : super core::Object::•()
+    ;
+}
+class W extends core::Object implements self::Z {
+  synthetic constructor •() → self::W
+    : super core::Object::•()
+    ;
+}
+class GX extends core::Object implements self::G<self::A> {
+  synthetic constructor •() → self::GX
+    : super core::Object::•()
+    ;
+}
+class GY extends self::X implements self::GB {
+  synthetic constructor •() → self::GY
+    : super self::X::•()
+    ;
+}
+class GZ extends core::Object implements self::Y, self::GC {
+  synthetic constructor •() → self::GZ
+    : super core::Object::•()
+    ;
+}
+class GW extends core::Object implements self::Z, self::GD {
+  synthetic constructor •() → self::GW
+    : super core::Object::•()
+    ;
+}
+class GU extends self::GW {
+  synthetic constructor •() → self::GU
+    : super self::GW::•()
+    ;
+}
+class GV extends self::GU implements self::GW {
+  synthetic constructor •() → self::GV
+    : super self::GU::•()
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/complex_class_hierarchy.dart.legacy.transformed.expect b/pkg/front_end/testcases/complex_class_hierarchy.dart.legacy.transformed.expect
new file mode 100644
index 0000000..faa6b6d
--- /dev/null
+++ b/pkg/front_end/testcases/complex_class_hierarchy.dart.legacy.transformed.expect
@@ -0,0 +1,95 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+class C extends self::B {
+  synthetic constructor •() → self::C
+    : super self::B::•()
+    ;
+}
+class D extends self::C {
+  synthetic constructor •() → self::D
+    : super self::C::•()
+    ;
+}
+class G<T extends self::A = dynamic> extends core::Object {
+  synthetic constructor •() → self::G<self::G::T>
+    : super core::Object::•()
+    ;
+}
+class GB extends self::G<self::B> {
+  synthetic constructor •() → self::GB
+    : super self::G::•()
+    ;
+}
+class GC extends self::G<self::C> {
+  synthetic constructor •() → self::GC
+    : super self::G::•()
+    ;
+}
+class GD extends self::G<self::D> {
+  synthetic constructor •() → self::GD
+    : super self::G::•()
+    ;
+}
+class X extends core::Object implements self::A {
+  synthetic constructor •() → self::X
+    : super core::Object::•()
+    ;
+}
+class Y extends self::X {
+  synthetic constructor •() → self::Y
+    : super self::X::•()
+    ;
+}
+class Z extends core::Object implements self::Y {
+  synthetic constructor •() → self::Z
+    : super core::Object::•()
+    ;
+}
+class W extends core::Object implements self::Z {
+  synthetic constructor •() → self::W
+    : super core::Object::•()
+    ;
+}
+class GX extends core::Object implements self::G<self::A> {
+  synthetic constructor •() → self::GX
+    : super core::Object::•()
+    ;
+}
+class GY extends self::X implements self::GB {
+  synthetic constructor •() → self::GY
+    : super self::X::•()
+    ;
+}
+class GZ extends core::Object implements self::Y, self::GC {
+  synthetic constructor •() → self::GZ
+    : super core::Object::•()
+    ;
+}
+class GW extends core::Object implements self::Z, self::GD {
+  synthetic constructor •() → self::GW
+    : super core::Object::•()
+    ;
+}
+class GU extends self::GW {
+  synthetic constructor •() → self::GU
+    : super self::GW::•()
+    ;
+}
+class GV extends self::GU implements self::GW {
+  synthetic constructor •() → self::GV
+    : super self::GU::•()
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/complex_class_hierarchy.dart.outline.expect b/pkg/front_end/testcases/complex_class_hierarchy.dart.outline.expect
new file mode 100644
index 0000000..f1265b2
--- /dev/null
+++ b/pkg/front_end/testcases/complex_class_hierarchy.dart.outline.expect
@@ -0,0 +1,78 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    ;
+}
+class C extends self::B {
+  synthetic constructor •() → self::C
+    ;
+}
+class D extends self::C {
+  synthetic constructor •() → self::D
+    ;
+}
+class G<T extends self::A = dynamic> extends core::Object {
+  synthetic constructor •() → self::G<self::G::T>
+    ;
+}
+class GB extends self::G<self::B> {
+  synthetic constructor •() → self::GB
+    ;
+}
+class GC extends self::G<self::C> {
+  synthetic constructor •() → self::GC
+    ;
+}
+class GD extends self::G<self::D> {
+  synthetic constructor •() → self::GD
+    ;
+}
+class X extends core::Object implements self::A {
+  synthetic constructor •() → self::X
+    ;
+}
+class Y extends self::X {
+  synthetic constructor •() → self::Y
+    ;
+}
+class Z extends core::Object implements self::Y {
+  synthetic constructor •() → self::Z
+    ;
+}
+class W extends core::Object implements self::Z {
+  synthetic constructor •() → self::W
+    ;
+}
+class GX extends core::Object implements self::G<self::A> {
+  synthetic constructor •() → self::GX
+    ;
+}
+class GY extends self::X implements self::GB {
+  synthetic constructor •() → self::GY
+    ;
+}
+class GZ extends core::Object implements self::Y, self::GC {
+  synthetic constructor •() → self::GZ
+    ;
+}
+class GW extends core::Object implements self::Z, self::GD {
+  synthetic constructor •() → self::GW
+    ;
+}
+class GU extends self::GW {
+  synthetic constructor •() → self::GU
+    ;
+}
+class GV extends self::GU implements self::GW {
+  synthetic constructor •() → self::GV
+    ;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/complex_class_hierarchy.dart.strong.expect b/pkg/front_end/testcases/complex_class_hierarchy.dart.strong.expect
new file mode 100644
index 0000000..1bca20f
--- /dev/null
+++ b/pkg/front_end/testcases/complex_class_hierarchy.dart.strong.expect
@@ -0,0 +1,95 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+class C extends self::B {
+  synthetic constructor •() → self::C
+    : super self::B::•()
+    ;
+}
+class D extends self::C {
+  synthetic constructor •() → self::D
+    : super self::C::•()
+    ;
+}
+class G<T extends self::A = self::A> extends core::Object {
+  synthetic constructor •() → self::G<self::G::T>
+    : super core::Object::•()
+    ;
+}
+class GB extends self::G<self::B> {
+  synthetic constructor •() → self::GB
+    : super self::G::•()
+    ;
+}
+class GC extends self::G<self::C> {
+  synthetic constructor •() → self::GC
+    : super self::G::•()
+    ;
+}
+class GD extends self::G<self::D> {
+  synthetic constructor •() → self::GD
+    : super self::G::•()
+    ;
+}
+class X extends core::Object implements self::A {
+  synthetic constructor •() → self::X
+    : super core::Object::•()
+    ;
+}
+class Y extends self::X {
+  synthetic constructor •() → self::Y
+    : super self::X::•()
+    ;
+}
+class Z extends core::Object implements self::Y {
+  synthetic constructor •() → self::Z
+    : super core::Object::•()
+    ;
+}
+class W extends core::Object implements self::Z {
+  synthetic constructor •() → self::W
+    : super core::Object::•()
+    ;
+}
+class GX extends core::Object implements self::G<self::A> {
+  synthetic constructor •() → self::GX
+    : super core::Object::•()
+    ;
+}
+class GY extends self::X implements self::GB {
+  synthetic constructor •() → self::GY
+    : super self::X::•()
+    ;
+}
+class GZ extends core::Object implements self::Y, self::GC {
+  synthetic constructor •() → self::GZ
+    : super core::Object::•()
+    ;
+}
+class GW extends core::Object implements self::Z, self::GD {
+  synthetic constructor •() → self::GW
+    : super core::Object::•()
+    ;
+}
+class GU extends self::GW {
+  synthetic constructor •() → self::GU
+    : super self::GW::•()
+    ;
+}
+class GV extends self::GU implements self::GW {
+  synthetic constructor •() → self::GV
+    : super self::GU::•()
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/complex_class_hierarchy.dart.strong.transformed.expect b/pkg/front_end/testcases/complex_class_hierarchy.dart.strong.transformed.expect
new file mode 100644
index 0000000..1bca20f
--- /dev/null
+++ b/pkg/front_end/testcases/complex_class_hierarchy.dart.strong.transformed.expect
@@ -0,0 +1,95 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+class C extends self::B {
+  synthetic constructor •() → self::C
+    : super self::B::•()
+    ;
+}
+class D extends self::C {
+  synthetic constructor •() → self::D
+    : super self::C::•()
+    ;
+}
+class G<T extends self::A = self::A> extends core::Object {
+  synthetic constructor •() → self::G<self::G::T>
+    : super core::Object::•()
+    ;
+}
+class GB extends self::G<self::B> {
+  synthetic constructor •() → self::GB
+    : super self::G::•()
+    ;
+}
+class GC extends self::G<self::C> {
+  synthetic constructor •() → self::GC
+    : super self::G::•()
+    ;
+}
+class GD extends self::G<self::D> {
+  synthetic constructor •() → self::GD
+    : super self::G::•()
+    ;
+}
+class X extends core::Object implements self::A {
+  synthetic constructor •() → self::X
+    : super core::Object::•()
+    ;
+}
+class Y extends self::X {
+  synthetic constructor •() → self::Y
+    : super self::X::•()
+    ;
+}
+class Z extends core::Object implements self::Y {
+  synthetic constructor •() → self::Z
+    : super core::Object::•()
+    ;
+}
+class W extends core::Object implements self::Z {
+  synthetic constructor •() → self::W
+    : super core::Object::•()
+    ;
+}
+class GX extends core::Object implements self::G<self::A> {
+  synthetic constructor •() → self::GX
+    : super core::Object::•()
+    ;
+}
+class GY extends self::X implements self::GB {
+  synthetic constructor •() → self::GY
+    : super self::X::•()
+    ;
+}
+class GZ extends core::Object implements self::Y, self::GC {
+  synthetic constructor •() → self::GZ
+    : super core::Object::•()
+    ;
+}
+class GW extends core::Object implements self::Z, self::GD {
+  synthetic constructor •() → self::GW
+    : super core::Object::•()
+    ;
+}
+class GU extends self::GW {
+  synthetic constructor •() → self::GU
+    : super self::GW::•()
+    ;
+}
+class GV extends self::GU implements self::GW {
+  synthetic constructor •() → self::GV
+    : super self::GU::•()
+    ;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 669a0f6..aebb6da 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -59,6 +59,7 @@
 clone_function_type: TextSerializationFailure # Was: Pass
 closure: TextSerializationFailure # Was: Pass
 co19_language_metadata_syntax_t04: TextSerializationFailure # Was: Pass
+complex_class_hierarchy: TextSerializationFailure
 constructor_const_inference: TextSerializationFailure # Was: Pass
 constructor_cycle: TextSerializationFailure # Was: Pass
 constructor_function_types: TextSerializationFailure # Was: Pass