Sort classes topologically when checking for cycles

Change-Id: I4d2fd8a1d3f2d369e69ed93665ce43ca4747b02d
Reviewed-on: https://dart-review.googlesource.com/c/86204
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/dill/dill_target.dart b/pkg/front_end/lib/src/fasta/dill/dill_target.dart
index bae16f6..63348b0 100644
--- a/pkg/front_end/lib/src/fasta/dill/dill_target.dart
+++ b/pkg/front_end/lib/src/fasta/dill/dill_target.dart
@@ -58,8 +58,5 @@
   }
 
   @override
-  void addDirectSupertype(ClassBuilder cls, Set<ClassBuilder> set) {}
-
-  @override
   void breakCycle(ClassBuilder cls) {}
 }
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 8381a1e..9f88c43 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -1754,27 +1754,24 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<Message Function(String name, String string)>
-    templateCyclicClassHierarchy =
-    const Template<Message Function(String name, String string)>(
-        messageTemplate: r"""'#name' is a supertype of itself via '#string'.""",
+const Template<Message Function(String name)> templateCyclicClassHierarchy =
+    const Template<Message Function(String name)>(
+        messageTemplate: r"""'#name' is a supertype of itself.""",
         withArguments: _withArgumentsCyclicClassHierarchy);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String name, String string)>
-    codeCyclicClassHierarchy =
-    const Code<Message Function(String name, String string)>(
+const Code<Message Function(String name)> codeCyclicClassHierarchy =
+    const Code<Message Function(String name)>(
         "CyclicClassHierarchy", templateCyclicClassHierarchy,
         analyzerCodes: <String>["RECURSIVE_INTERFACE_INHERITANCE"]);
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsCyclicClassHierarchy(String name, String string) {
+Message _withArgumentsCyclicClassHierarchy(String name) {
   if (name.isEmpty) throw 'No name provided';
   name = demangleMixinApplicationName(name);
-  if (string.isEmpty) throw 'No string provided';
   return new Message(codeCyclicClassHierarchy,
-      message: """'${name}' is a supertype of itself via '${string}'.""",
-      arguments: {'name': name, 'string': string});
+      message: """'${name}' is a supertype of itself.""",
+      arguments: {'name': name});
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -2044,28 +2041,6 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<Message Function(String name)>
-    templateDirectCyclicClassHierarchy =
-    const Template<Message Function(String name)>(
-        messageTemplate: r"""'#name' can't use itself as a supertype.""",
-        withArguments: _withArgumentsDirectCyclicClassHierarchy);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String name)> codeDirectCyclicClassHierarchy =
-    const Code<Message Function(String name)>(
-        "DirectCyclicClassHierarchy", templateDirectCyclicClassHierarchy,
-        analyzerCodes: <String>["RECURSIVE_INTERFACE_INHERITANCE"]);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsDirectCyclicClassHierarchy(String name) {
-  if (name.isEmpty) throw 'No name provided';
-  name = demangleMixinApplicationName(name);
-  return new Message(codeDirectCyclicClassHierarchy,
-      message: """'${name}' can't use itself as a supertype.""",
-      arguments: {'name': name});
-}
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeDirectiveAfterDeclaration =
     messageDirectiveAfterDeclaration;
 
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 3fc17fc..b31d999 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -197,37 +197,6 @@
     return new KernelLibraryBuilder(uri, fileUri, loader, origin);
   }
 
-  void forEachDirectSupertype(ClassBuilder cls, void f(NamedTypeBuilder type)) {
-    TypeBuilder supertype = cls.supertype;
-    if (supertype is NamedTypeBuilder) {
-      f(supertype);
-    } else if (supertype != null) {
-      unhandled("${supertype.runtimeType}", "forEachDirectSupertype",
-          cls.charOffset, cls.fileUri);
-    }
-    if (cls.interfaces != null) {
-      for (NamedTypeBuilder t in cls.interfaces) {
-        f(t);
-      }
-    }
-    if (cls.library.loader == loader &&
-        // TODO(ahe): Implement DillClassBuilder.mixedInType and remove the
-        // above check.
-        cls.mixedInType != null) {
-      f(cls.mixedInType);
-    }
-  }
-
-  void addDirectSupertype(ClassBuilder cls, Set<ClassBuilder> set) {
-    if (cls == null) return;
-    forEachDirectSupertype(cls, (NamedTypeBuilder type) {
-      Declaration declaration = type.declaration;
-      if (declaration is ClassBuilder) {
-        set.add(declaration);
-      }
-    });
-  }
-
   /// Returns classes defined in libraries in [loader].
   List<SourceClassBuilder> collectMyClasses() {
     List<SourceClassBuilder> result = <SourceClassBuilder>[];
@@ -270,8 +239,8 @@
       bottomType.bind(loader.coreLibrary["Null"]);
       loader.resolveTypes();
       loader.computeDefaultTypes(dynamicType, bottomType, objectClassBuilder);
-      List<SourceClassBuilder> myClasses = collectMyClasses();
-      loader.checkSemantics(myClasses, objectClassBuilder);
+      List<SourceClassBuilder> myClasses =
+          loader.checkSemantics(objectClassBuilder);
       loader.finishTypeVariables(objectClassBuilder, dynamicType);
       loader.buildComponent();
       installDefaultSupertypes();
@@ -412,7 +381,7 @@
   void installSyntheticConstructors(List<SourceClassBuilder> builders) {
     Class objectClass = this.objectClass;
     for (SourceClassBuilder builder in builders) {
-      if (builder.target != objectClass) {
+      if (builder.target != objectClass && !builder.isPatch) {
         if (builder.isPatch || builder.isMixinDeclaration) continue;
         if (builder.isMixinApplication) {
           installForwardingConstructors(builder);
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index 0c507c5..0691889 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -24,12 +24,14 @@
 
 import '../kernel/kernel_builder.dart'
     show
+        ClassBuilder,
         ConstructorReferenceBuilder,
         Declaration,
         KernelClassBuilder,
         KernelFieldBuilder,
         KernelFunctionBuilder,
         KernelLibraryBuilder,
+        KernelNamedTypeBuilder,
         KernelTypeBuilder,
         KernelTypeVariableBuilder,
         LibraryBuilder,
@@ -69,7 +71,8 @@
   return cls;
 }
 
-class SourceClassBuilder extends KernelClassBuilder {
+class SourceClassBuilder extends KernelClassBuilder
+    implements Comparable<SourceClassBuilder> {
   @override
   final Class actualCls;
 
@@ -247,7 +250,9 @@
         declaration.prepareTopLevelInference();
       }
     });
-    cls.setupApiMembers(library.loader.interfaceResolver);
+    if (!isPatch) {
+      cls.setupApiMembers(library.loader.interfaceResolver);
+    }
   }
 
   @override
@@ -274,4 +279,33 @@
     });
     return count;
   }
+
+  List<Declaration> computeDirectSupertypes(ClassBuilder objectClass) {
+    final List<Declaration> result = <Declaration>[];
+    final KernelNamedTypeBuilder supertype = this.supertype;
+    if (supertype != null) {
+      result.add(supertype.declaration);
+    } else if (objectClass != this) {
+      result.add(objectClass);
+    }
+    final List<KernelTypeBuilder> interfaces = this.interfaces;
+    if (interfaces != null) {
+      for (int i = 0; i < interfaces.length; i++) {
+        KernelNamedTypeBuilder interface = interfaces[i];
+        result.add(interface.declaration);
+      }
+    }
+    final KernelNamedTypeBuilder mixedInType = this.mixedInType;
+    if (mixedInType != null) {
+      result.add(mixedInType.declaration);
+    }
+    return result;
+  }
+
+  @override
+  int compareTo(SourceClassBuilder other) {
+    int result = "$fileUri".compareTo("${other.fileUri}");
+    if (result != 0) return result;
+    return charOffset.compareTo(other.charOffset);
+  }
 }
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 5edde47..35aade6 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -66,7 +66,6 @@
         templateIllegalMixinDueToConstructorsCause,
         templateInternalProblemUriMissingScheme,
         templateSourceOutlineSummary,
-        templateDirectCyclicClassHierarchy,
         templateUntranslatableUri;
 
 import '../fasta_codes.dart' as fasta_codes;
@@ -95,7 +94,7 @@
 
 import '../parser.dart' show Parser, lengthForToken, offsetForToken;
 
-import '../problems.dart' show internalProblem, unexpected, unhandled;
+import '../problems.dart' show internalProblem, unhandled;
 
 import '../scanner.dart' show ErrorToken, ScannerResult, Token, scan;
 
@@ -142,8 +141,6 @@
 
   Instrumentation instrumentation;
 
-  List<ClassBuilder> orderedClasses;
-
   SourceLoader(this.fileSystem, this.includeComments, KernelTarget target)
       : super(target);
 
@@ -504,57 +501,6 @@
     ticker.logMs("Finished $count patch methods");
   }
 
-  /// Returns all the supertypes (including interfaces) of [cls]
-  /// transitively. Includes [cls].
-  Set<ClassBuilder> allSupertypes(ClassBuilder cls) {
-    int length = 0;
-    Set<ClassBuilder> result = new Set<ClassBuilder>()..add(cls);
-    while (length != result.length) {
-      length = result.length;
-      result.addAll(directSupertypes(result));
-    }
-    return result;
-  }
-
-  /// Returns the direct supertypes (including interface) of [classes]. A class
-  /// from [classes] is only included if it is a supertype of one of the other
-  /// classes in [classes].
-  Set<ClassBuilder> directSupertypes(Iterable<ClassBuilder> classes) {
-    Set<ClassBuilder> result = new Set<ClassBuilder>();
-    for (ClassBuilder cls in classes) {
-      target.addDirectSupertype(cls, result);
-    }
-    return result;
-  }
-
-  /// Computes a set of classes that may have cycles. The set is empty if there
-  /// are no cycles. If the set isn't empty, it will include supertypes of
-  /// classes with cycles, as well as the classes with cycles.
-  ///
-  /// It is assumed that [classes] is a transitive closure with respect to
-  /// supertypes.
-  Iterable<ClassBuilder> cyclicCandidates(Iterable<ClassBuilder> classes) {
-    // The candidates are found by a fixed-point computation.
-    //
-    // On each iteration, the classes that have no supertypes in the input set
-    // will be removed.
-    //
-    // If there are no cycles, eventually, the set will converge on Object, and
-    // the next iteration will make the set empty (as Object has no
-    // supertypes).
-    //
-    // On the other hand, if there is a cycle, the cycle will remain in the
-    // set, and so will its supertypes, and eventually the input and output set
-    // will have the same length.
-    Iterable<ClassBuilder> input = const [];
-    Iterable<ClassBuilder> output = classes;
-    while (input.length != output.length) {
-      input = output;
-      output = directSupertypes(input);
-    }
-    return output;
-  }
-
   /// Check that [objectClass] has no supertypes. Recover by removing any
   /// found.
   void checkObjectClassHierarchy(ClassBuilder objectClass) {
@@ -578,111 +524,141 @@
     }
   }
 
-  void checkSemantics(
-      List<SourceClassBuilder> classes, ClassBuilder objectClass) {
-    checkObjectClassHierarchy(objectClass);
-    Iterable<ClassBuilder> candidates = cyclicCandidates(classes);
-    if (candidates.isNotEmpty) {
-      Map<ClassBuilder, Set<ClassBuilder>> realCycles =
-          <ClassBuilder, Set<ClassBuilder>>{};
-      for (ClassBuilder cls in candidates) {
-        Set<ClassBuilder> cycles = cyclicCandidates(allSupertypes(cls));
-        if (cycles.isNotEmpty) {
-          realCycles[cls] = cycles;
-        }
-      }
-      Map<LocatedMessage, ClassBuilder> messages =
-          <LocatedMessage, ClassBuilder>{};
-      realCycles.forEach((ClassBuilder cls, Set<ClassBuilder> cycles) {
-        target.breakCycle(cls);
-        List<ClassBuilder> involved = <ClassBuilder>[];
-        for (ClassBuilder cls in cycles) {
-          if (realCycles.containsKey(cls)) {
-            involved.add(cls);
+  /// Returns a list of all class builders declared in this loader.  As the
+  /// classes are sorted, any cycles in the hiearchy are reported as
+  /// errors. Recover by breaking the cycles. This means that the rest of the
+  /// pipeline (including backends) can assume that there are no hierarchy
+  /// cycles.
+  List<SourceClassBuilder> handleHierarchyCycles(ClassBuilder objectClass) {
+    // Compute the initial work list of all classes declared in this loader.
+    List<SourceClassBuilder> workList = <SourceClassBuilder>[];
+    for (LibraryBuilder library in builders.values) {
+      if (library.loader == this) {
+        Iterator<Declaration> members = library.iterator;
+        while (members.moveNext()) {
+          Declaration member = members.current;
+          if (member is SourceClassBuilder) {
+            workList.add(member);
           }
         }
-        // Sort the class names alphabetically to ensure the order is stable.
-        // TODO(ahe): It's possible that a better UX would be to sort the
-        // classes based on walking the class hierarchy in breadth-first order.
-        String involvedString = (involved
-                .where((c) => c != cls)
-                .map((c) => c.fullNameForErrors)
-                .toList()
-                  ..sort())
-            .join("', '");
-        LocatedMessage message = involvedString.isEmpty
-            ? templateDirectCyclicClassHierarchy
-                .withArguments(cls.fullNameForErrors)
-                .withLocation(cls.fileUri, cls.charOffset, noLength)
-            : templateCyclicClassHierarchy
-                .withArguments(cls.fullNameForErrors, involvedString)
-                .withLocation(cls.fileUri, cls.charOffset, noLength);
-        messages[message] = cls;
-      });
-
-      // Report all classes involved in a cycle, sorted to ensure stability as
-      // [cyclicCandidates] is sensitive to if the platform (or other modules)
-      // are included in [classes].
-      for (LocatedMessage message in messages.keys.toList()..sort()) {
-        messages[message].addProblem(
-            message.messageObject, message.charOffset, message.length);
       }
     }
-    ticker.logMs("Found cycles");
+
     Set<ClassBuilder> blackListedClasses = new Set<ClassBuilder>();
     for (int i = 0; i < blacklistedCoreClasses.length; i++) {
       blackListedClasses.add(coreLibrary[blacklistedCoreClasses[i]]);
     }
-    for (ClassBuilder cls in classes) {
-      if (cls.library.loader != this) continue;
-      Set<ClassBuilder> directSupertypes = new Set<ClassBuilder>();
-      target.addDirectSupertype(cls, directSupertypes);
-      for (ClassBuilder supertype in directSupertypes) {
-        if (supertype is EnumBuilder) {
-          cls.addProblem(templateExtendingEnum.withArguments(supertype.name),
-              cls.charOffset, noLength);
-        } else if (!cls.library.mayImplementRestrictedTypes &&
-            blackListedClasses.contains(supertype)) {
-          cls.addProblem(
-              templateExtendingRestricted.withArguments(supertype.name),
-              cls.charOffset,
-              noLength);
+
+    // Sort the classes topologically.
+    Set<SourceClassBuilder> topologicallySortedClasses =
+        new Set<SourceClassBuilder>();
+    List<SourceClassBuilder> previousWorkList;
+    do {
+      previousWorkList = workList;
+      workList = <SourceClassBuilder>[];
+      for (int i = 0; i < previousWorkList.length; i++) {
+        SourceClassBuilder cls = previousWorkList[i];
+        List<Declaration> directSupertypes =
+            cls.computeDirectSupertypes(objectClass);
+        bool allSupertypesProcessed = true;
+        for (int i = 0; i < directSupertypes.length; i++) {
+          Declaration supertype = directSupertypes[i];
+          if (supertype is SourceClassBuilder &&
+              supertype.library.loader == this &&
+              !topologicallySortedClasses.contains(supertype)) {
+            allSupertypesProcessed = false;
+            break;
+          }
+        }
+        if (allSupertypesProcessed) {
+          topologicallySortedClasses.add(cls);
+          checkClassSupertypes(cls, directSupertypes, blackListedClasses);
+        } else {
+          workList.add(cls);
         }
       }
-      TypeBuilder mixedInType = cls.mixedInType;
-      if (mixedInType != null) {
-        bool isClassBuilder = false;
-        if (mixedInType is NamedTypeBuilder) {
-          var builder = mixedInType.declaration;
-          if (builder is ClassBuilder) {
-            isClassBuilder = true;
-            for (Declaration constructory
-                in builder.constructors.local.values) {
-              if (constructory.isConstructor && !constructory.isSynthetic) {
-                cls.addProblem(
-                    templateIllegalMixinDueToConstructors
-                        .withArguments(builder.fullNameForErrors),
-                    cls.charOffset,
-                    noLength,
-                    context: [
-                      templateIllegalMixinDueToConstructorsCause
-                          .withArguments(builder.fullNameForErrors)
-                          .withLocation(constructory.fileUri,
-                              constructory.charOffset, noLength)
-                    ]);
-              }
+    } while (previousWorkList.length != workList.length);
+    List<SourceClassBuilder> classes = topologicallySortedClasses.toList();
+    List<SourceClassBuilder> classesWithCycles = previousWorkList;
+
+    // Once the work list doesn't change in size, it's either empty, or
+    // contains all classes with cycles.
+
+    // Sort the classes to ensure consistent output.
+    classesWithCycles.sort();
+    for (int i = 0; i < classesWithCycles.length; i++) {
+      SourceClassBuilder cls = classesWithCycles[i];
+      target.breakCycle(cls);
+      classes.add(cls);
+      cls.addProblem(
+          templateCyclicClassHierarchy.withArguments(cls.fullNameForErrors),
+          cls.charOffset,
+          noLength);
+    }
+
+    ticker.logMs("Checked class hierarchy");
+    return classes;
+  }
+
+  void checkClassSupertypes(
+      SourceClassBuilder cls,
+      List<Declaration> directSupertypes,
+      Set<ClassBuilder> blackListedClasses) {
+    // Check that the direct supertypes aren't black-listed or enums.
+    for (int i = 0; i < directSupertypes.length; i++) {
+      Declaration supertype = directSupertypes[i];
+      if (supertype is EnumBuilder) {
+        cls.addProblem(templateExtendingEnum.withArguments(supertype.name),
+            cls.charOffset, noLength);
+      } else if (!cls.library.mayImplementRestrictedTypes &&
+          blackListedClasses.contains(supertype)) {
+        cls.addProblem(
+            templateExtendingRestricted
+                .withArguments(supertype.fullNameForErrors),
+            cls.charOffset,
+            noLength);
+      }
+    }
+
+    // Check that the mixed-in type can be used as a mixin.
+    final TypeBuilder mixedInType = cls.mixedInType;
+    if (mixedInType != null) {
+      bool isClassBuilder = false;
+      if (mixedInType is NamedTypeBuilder) {
+        var builder = mixedInType.declaration;
+        if (builder is ClassBuilder) {
+          isClassBuilder = true;
+          for (Declaration constructory in builder.constructors.local.values) {
+            if (constructory.isConstructor && !constructory.isSynthetic) {
+              cls.addProblem(
+                  templateIllegalMixinDueToConstructors
+                      .withArguments(builder.fullNameForErrors),
+                  cls.charOffset,
+                  noLength,
+                  context: [
+                    templateIllegalMixinDueToConstructorsCause
+                        .withArguments(builder.fullNameForErrors)
+                        .withLocation(constructory.fileUri,
+                            constructory.charOffset, noLength)
+                  ]);
             }
           }
         }
-        if (!isClassBuilder) {
-          cls.addProblem(
-              templateIllegalMixin.withArguments(mixedInType.fullNameForErrors),
-              cls.charOffset,
-              noLength);
-        }
+      }
+      if (!isClassBuilder) {
+        // TODO(ahe): Either we need to check this for superclass and
+        // interfaces, or this shouldn't be necessary (or handled elsewhere).
+        cls.addProblem(
+            templateIllegalMixin.withArguments(mixedInType.fullNameForErrors),
+            cls.charOffset,
+            noLength);
       }
     }
-    ticker.logMs("Checked restricted supertypes");
+  }
+
+  List<SourceClassBuilder> checkSemantics(ClassBuilder objectClass) {
+    checkObjectClassHierarchy(objectClass);
+    List<SourceClassBuilder> classes = handleHierarchyCycles(objectClass);
 
     // Check imports and exports for duplicate names.
     // This is rather silly, e.g. it makes importing 'foo' and exporting another
@@ -757,6 +733,7 @@
       }
     });
     ticker.logMs("Checked imports and exports for duplicate names");
+    return classes;
   }
 
   void buildComponent() {
@@ -847,7 +824,7 @@
 
   void checkSupertypes(List<SourceClassBuilder> sourceClasses) {
     for (SourceClassBuilder builder in sourceClasses) {
-      if (builder.library.loader == this) {
+      if (builder.library.loader == this && !builder.isPatch) {
         builder.checkSupertypes(coreTypes);
       }
     }
@@ -871,7 +848,7 @@
   void checkOverrides(List<SourceClassBuilder> sourceClasses) {
     assert(hierarchy != null);
     for (SourceClassBuilder builder in sourceClasses) {
-      if (builder.library.loader == this) {
+      if (builder.library.loader == this && !builder.isPatch) {
         builder.checkOverrides(
             hierarchy, typeInferenceEngine?.typeSchemaEnvironment);
       }
@@ -883,7 +860,7 @@
     if (target.legacyMode) return;
     assert(hierarchy != null);
     for (SourceClassBuilder builder in sourceClasses) {
-      if (builder.library.loader == this) {
+      if (builder.library.loader == this && !builder.isPatch) {
         builder.checkAbstractMembers(
             coreTypes, hierarchy, typeInferenceEngine.typeSchemaEnvironment);
       }
@@ -894,7 +871,7 @@
   void checkRedirectingFactories(List<SourceClassBuilder> sourceClasses) {
     if (target.legacyMode) return;
     for (SourceClassBuilder builder in sourceClasses) {
-      if (builder.library.loader == this) {
+      if (builder.library.loader == this && !builder.isPatch) {
         builder.checkRedirectingFactories(
             typeInferenceEngine.typeSchemaEnvironment);
       }
@@ -907,7 +884,7 @@
 
     List<Class> changedClasses = new List<Class>();
     for (SourceClassBuilder builder in sourceClasses) {
-      if (builder.library.loader == this) {
+      if (builder.library.loader == this && !builder.isPatch) {
         if (builder.addNoSuchMethodForwarders(target, hierarchy)) {
           changedClasses.add(builder.target);
         }
@@ -919,7 +896,7 @@
 
   void checkMixins(List<SourceClassBuilder> sourceClasses) {
     for (SourceClassBuilder builder in sourceClasses) {
-      if (builder.library.loader == this) {
+      if (builder.library.loader == this && !builder.isPatch) {
         if (builder.isMixinDeclaration) {
           builder.checkMixinDeclaration();
         }
@@ -962,27 +939,8 @@
         }
       }
     }
-    {
-      // Note: we need to create a list before iterating, since calling
-      // builder.prepareTopLevelInference causes further class hierarchy
-      // queries to be made which would otherwise result in a concurrent
-      // modification exception.
-      List<Class> classes = new List<Class>(sourceClasses.length);
-      for (int i = 0; i < sourceClasses.length; i++) {
-        classes[i] = sourceClasses[i].target;
-      }
-      orderedClasses = null;
-      List<ClassBuilder> result = new List<ClassBuilder>(sourceClasses.length);
-      int i = 0;
-      for (Class cls
-          in new List<Class>.from(hierarchy.getOrderedClasses(classes))) {
-        result[i++] = ShadowClass.getClassInferenceInfo(cls).builder
-          ..prepareTopLevelInference();
-      }
-      if (i != result.length) {
-        unexpected("${result.length}", "$i", -1, null);
-      }
-      orderedClasses = result;
+    for (int i = 0; i < sourceClasses.length; i++) {
+      sourceClasses[i].prepareTopLevelInference();
     }
     typeInferenceEngine.isTypeInferencePrepared = true;
     ticker.logMs("Prepared top level inference");
@@ -992,7 +950,8 @@
     /// their types.
     typeInferenceEngine.finishTopLevelFields();
     List<Class> changedClasses = new List<Class>();
-    for (var builder in orderedClasses) {
+    for (var builder in sourceClasses) {
+      if (builder.isPatch) continue;
       ShadowClass class_ = builder.target;
       int memberCount = class_.fields.length +
           class_.constructors.length +
@@ -1012,7 +971,6 @@
       }
     }
 
-    orderedClasses = null;
     typeInferenceEngine.finishTopLevelInitializingFormals();
     if (instrumentation != null) {
       builders.forEach((Uri uri, LibraryBuilder library) {
diff --git a/pkg/front_end/lib/src/fasta/target_implementation.dart b/pkg/front_end/lib/src/fasta/target_implementation.dart
index 08581aa..350a79a 100644
--- a/pkg/front_end/lib/src/fasta/target_implementation.dart
+++ b/pkg/front_end/lib/src/fasta/target_implementation.dart
@@ -46,9 +46,6 @@
   LibraryBuilder createLibraryBuilder(
       Uri uri, Uri fileUri, covariant LibraryBuilder origin);
 
-  /// Add the classes extended or implemented directly by [cls] to [set].
-  void addDirectSupertype(ClassBuilder cls, Set<ClassBuilder> set);
-
   /// The class [cls] is involved in a cyclic definition. This method should
   /// ensure that the cycle is broken, for example, by removing superclass and
   /// implemented interfaces.
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 3876a46..cebe774 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -1792,7 +1792,7 @@
     main.dart: "import 'lib1.dart'; import 'lib2.dart'; A a;"
 
 CyclicClassHierarchy:
-  template: "'#name' is a supertype of itself via '#string'."
+  template: "'#name' is a supertype of itself."
   analyzerCode: RECURSIVE_INTERFACE_INHERITANCE
   script:
     - |
@@ -1801,11 +1801,6 @@
     - |
       class A implements B {}
       class B implements A {}
-
-DirectCyclicClassHierarchy:
-  template: "'#name' can't use itself as a supertype."
-  analyzerCode: RECURSIVE_INTERFACE_INHERITANCE
-  script:
     - "class C = Object with C;"
     - "class C extends C {}"
     - "class C implements C {}"
diff --git a/pkg/front_end/testcases/cycles.dart.legacy.expect b/pkg/front_end/testcases/cycles.dart.legacy.expect
index d2e8cdf..4ee0331 100644
--- a/pkg/front_end/testcases/cycles.dart.legacy.expect
+++ b/pkg/front_end/testcases/cycles.dart.legacy.expect
@@ -1,28 +1,28 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself via 'B', 'C'.
+// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself.
 // class A implements C {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself via 'A', 'C'.
+// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself.
 // class B extends A {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself via 'A', 'B'.
+// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself.
 // class C extends B implements D {}
 //       ^
 
 // Unhandled errors:
 //
-// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself via 'B', 'C'.
+// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself.
 // class A implements C {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself via 'A', 'C'.
+// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself.
 // class B extends A {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself via 'A', 'B'.
+// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself.
 // class C extends B implements D {}
 //       ^
 
diff --git a/pkg/front_end/testcases/cycles.dart.legacy.transformed.expect b/pkg/front_end/testcases/cycles.dart.legacy.transformed.expect
index 435c417..08a24b3 100644
--- a/pkg/front_end/testcases/cycles.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/cycles.dart.legacy.transformed.expect
@@ -1,14 +1,14 @@
 // Unhandled errors:
 //
-// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself via 'B', 'C'.
+// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself.
 // class A implements C {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself via 'A', 'C'.
+// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself.
 // class B extends A {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself via 'A', 'B'.
+// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself.
 // class C extends B implements D {}
 //       ^
 
diff --git a/pkg/front_end/testcases/cycles.dart.outline.expect b/pkg/front_end/testcases/cycles.dart.outline.expect
index 3aaceb9..b1b0614 100644
--- a/pkg/front_end/testcases/cycles.dart.outline.expect
+++ b/pkg/front_end/testcases/cycles.dart.outline.expect
@@ -1,14 +1,14 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself via 'B', 'C'.
+// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself.
 // class A implements C {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself via 'A', 'C'.
+// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself.
 // class B extends A {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself via 'A', 'B'.
+// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself.
 // class C extends B implements D {}
 //       ^
 
diff --git a/pkg/front_end/testcases/cycles.dart.strong.expect b/pkg/front_end/testcases/cycles.dart.strong.expect
index d2e8cdf..4ee0331 100644
--- a/pkg/front_end/testcases/cycles.dart.strong.expect
+++ b/pkg/front_end/testcases/cycles.dart.strong.expect
@@ -1,28 +1,28 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself via 'B', 'C'.
+// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself.
 // class A implements C {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself via 'A', 'C'.
+// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself.
 // class B extends A {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself via 'A', 'B'.
+// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself.
 // class C extends B implements D {}
 //       ^
 
 // Unhandled errors:
 //
-// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself via 'B', 'C'.
+// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself.
 // class A implements C {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself via 'A', 'C'.
+// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself.
 // class B extends A {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself via 'A', 'B'.
+// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself.
 // class C extends B implements D {}
 //       ^
 
diff --git a/pkg/front_end/testcases/cycles.dart.strong.transformed.expect b/pkg/front_end/testcases/cycles.dart.strong.transformed.expect
index 435c417..08a24b3 100644
--- a/pkg/front_end/testcases/cycles.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/cycles.dart.strong.transformed.expect
@@ -1,14 +1,14 @@
 // Unhandled errors:
 //
-// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself via 'B', 'C'.
+// pkg/front_end/testcases/cycles.dart:5:7: Error: 'A' is a supertype of itself.
 // class A implements C {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself via 'A', 'C'.
+// pkg/front_end/testcases/cycles.dart:7:7: Error: 'B' is a supertype of itself.
 // class B extends A {}
 //       ^
 //
-// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself via 'A', 'B'.
+// pkg/front_end/testcases/cycles.dart:9:7: Error: 'C' is a supertype of itself.
 // class C extends B implements D {}
 //       ^
 
diff --git a/pkg/front_end/testcases/incremental_bulk_compiler_smoke.status b/pkg/front_end/testcases/incremental_bulk_compiler_smoke.status
index 4240855..959cc51 100644
--- a/pkg/front_end/testcases/incremental_bulk_compiler_smoke.status
+++ b/pkg/front_end/testcases/incremental_bulk_compiler_smoke.status
@@ -1,9 +1,8 @@
 # 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.md file.
+
 language_2/deferred_super_dependency_test: Crash # missing "#errors"
 language_2/missing_part_of_tag_test: Crash # missing empty library that shouldn't have been there in the first place
-language_2/regress_27957_test: Crash # isSynthetic becomes false on C1 SuperInitializer.
 language_2/script1_negative_test: Crash # missing "#errors", missing empty library that shouldn't have been there in the first place
 language_2/script2_negative_test: Crash # missing "#errors", missing empty library that shouldn't have been there in the first place
-
diff --git a/pkg/front_end/testcases/inference/circular_method_inference.dart.legacy.expect b/pkg/front_end/testcases/inference/circular_method_inference.dart.legacy.expect
index d03e49e..658bd51 100644
--- a/pkg/front_end/testcases/inference/circular_method_inference.dart.legacy.expect
+++ b/pkg/front_end/testcases/inference/circular_method_inference.dart.legacy.expect
@@ -1,20 +1,20 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself via 'B'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself.
 // abstract class A extends B {
 //                ^
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself via 'A'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself.
 // abstract class B extends A {
 //                ^
 
 // Unhandled errors:
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself via 'B'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself.
 // abstract class A extends B {
 //                ^
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself via 'A'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself.
 // abstract class B extends A {
 //                ^
 
diff --git a/pkg/front_end/testcases/inference/circular_method_inference.dart.legacy.transformed.expect b/pkg/front_end/testcases/inference/circular_method_inference.dart.legacy.transformed.expect
index 8f3be84..fa9873c 100644
--- a/pkg/front_end/testcases/inference/circular_method_inference.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/inference/circular_method_inference.dart.legacy.transformed.expect
@@ -1,10 +1,10 @@
 // Unhandled errors:
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself via 'B'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself.
 // abstract class A extends B {
 //                ^
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself via 'A'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself.
 // abstract class B extends A {
 //                ^
 
diff --git a/pkg/front_end/testcases/inference/circular_method_inference.dart.outline.expect b/pkg/front_end/testcases/inference/circular_method_inference.dart.outline.expect
index 1cb6512..bd72e73 100644
--- a/pkg/front_end/testcases/inference/circular_method_inference.dart.outline.expect
+++ b/pkg/front_end/testcases/inference/circular_method_inference.dart.outline.expect
@@ -1,10 +1,10 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself via 'B'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself.
 // abstract class A extends B {
 //                ^
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself via 'A'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself.
 // abstract class B extends A {
 //                ^
 
diff --git a/pkg/front_end/testcases/inference/circular_method_inference.dart.strong.expect b/pkg/front_end/testcases/inference/circular_method_inference.dart.strong.expect
index d03e49e..658bd51 100644
--- a/pkg/front_end/testcases/inference/circular_method_inference.dart.strong.expect
+++ b/pkg/front_end/testcases/inference/circular_method_inference.dart.strong.expect
@@ -1,20 +1,20 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself via 'B'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself.
 // abstract class A extends B {
 //                ^
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself via 'A'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself.
 // abstract class B extends A {
 //                ^
 
 // Unhandled errors:
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself via 'B'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself.
 // abstract class A extends B {
 //                ^
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself via 'A'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself.
 // abstract class B extends A {
 //                ^
 
diff --git a/pkg/front_end/testcases/inference/circular_method_inference.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/circular_method_inference.dart.strong.transformed.expect
index 8f3be84..fa9873c 100644
--- a/pkg/front_end/testcases/inference/circular_method_inference.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/inference/circular_method_inference.dart.strong.transformed.expect
@@ -1,10 +1,10 @@
 // Unhandled errors:
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself via 'B'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:12:16: Error: 'A' is a supertype of itself.
 // abstract class A extends B {
 //                ^
 //
-// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself via 'A'.
+// pkg/front_end/testcases/inference/circular_method_inference.dart:16:16: Error: 'B' is a supertype of itself.
 // abstract class B extends A {
 //                ^
 
diff --git a/pkg/front_end/testcases/metadata_named_mixin_application.dart.outline.expect b/pkg/front_end/testcases/metadata_named_mixin_application.dart.outline.expect
index 1da773d..cec02ba 100644
--- a/pkg/front_end/testcases/metadata_named_mixin_application.dart.outline.expect
+++ b/pkg/front_end/testcases/metadata_named_mixin_application.dart.outline.expect
@@ -4,6 +4,7 @@
 
 class C = self::D with self::E {
   synthetic constructor •() → self::C
+    : super self::D::•()
     ;
 }
 class D extends core::Object {
diff --git a/pkg/front_end/testcases/qualified.dart.outline.expect b/pkg/front_end/testcases/qualified.dart.outline.expect
index 42db141..fb0b74d 100644
--- a/pkg/front_end/testcases/qualified.dart.outline.expect
+++ b/pkg/front_end/testcases/qualified.dart.outline.expect
@@ -32,6 +32,7 @@
 }
 abstract class _WithMixin&Supertype&Mixin = lib::Supertype with lib::Mixin {
   synthetic constructor •() → self::_WithMixin&Supertype&Mixin
+    : super lib::Supertype::•()
     ;
 }
 class WithMixin extends self::_WithMixin&Supertype&Mixin {