Mark Kernel classes that were Dart mixin declarations

Add a flag to Kernel classes that indicates that they were Dart mixin
declarations.  Also, add an API to Kernel Class that allows a list of
superclass constraints to be gotten from it.

Change-Id: Ie78e7e56b5421dfb9d340e4330135b8d6f4e94f1
Reviewed-on: https://dart-review.googlesource.com/75261
Commit-Queue: Kevin Millikin <kmillikin@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_enum_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_enum_builder.dart
index e1eaa25..1efee63 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_enum_builder.dart
@@ -88,7 +88,8 @@
       int startCharOffset,
       int charOffset)
       : super(metadata, 0, name, null, null, null, scope, constructors, parent,
-            null, startCharOffset, charOffset, TreeNode.noOffset, cls);
+            null, startCharOffset, charOffset, TreeNode.noOffset,
+            cls: cls);
 
   factory KernelEnumBuilder(
       MetadataCollector metadataCollector,
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
index 37562af..1ff9f83 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
@@ -62,7 +62,11 @@
 import '../loader.dart' show Loader;
 
 import '../modifier.dart'
-    show abstractMask, namedMixinApplicationMask, staticMask;
+    show
+        abstractMask,
+        mixinDeclarationMask,
+        namedMixinApplicationMask,
+        staticMask;
 
 import '../problems.dart' show unexpected, unhandled;
 
@@ -216,6 +220,11 @@
     // library scope.
     Scope constructorScope = new Scope(constructors, null, null, "constructors",
         isModifiable: false);
+    bool isMixinDeclaration = false;
+    if (modifiers & mixinDeclarationMask != 0) {
+      isMixinDeclaration = true;
+      modifiers = (modifiers & ~mixinDeclarationMask) | abstractMask;
+    }
     ClassBuilder cls = new SourceClassBuilder(
         metadata,
         modifiers,
@@ -230,7 +239,8 @@
         new List<ConstructorReferenceBuilder>.from(constructorReferences),
         startCharOffset,
         charOffset,
-        charEndOffset);
+        charEndOffset,
+        isMixinDeclaration: isMixinDeclaration);
     loader.target.metadataCollector
         ?.setDocumentationComment(cls.target, documentationComment);
 
@@ -470,8 +480,7 @@
             startCharOffset,
             charOffset,
             TreeNode.noOffset,
-            null,
-            mixin);
+            mixedInType: mixin);
         if (isNamedMixinApplication) {
           loader.target.metadataCollector?.setDocumentationComment(
               application.target, documentationComment);
diff --git a/pkg/front_end/lib/src/fasta/modifier.dart b/pkg/front_end/lib/src/fasta/modifier.dart
index 4c4cb84..52f7017 100644
--- a/pkg/front_end/lib/src/fasta/modifier.dart
+++ b/pkg/front_end/lib/src/fasta/modifier.dart
@@ -32,6 +32,10 @@
 
 const int namedMixinApplicationMask = staticMask << 1;
 
+/// Not a modifier, used for mixins declared explicitly by using the `mixin`
+/// keyword.
+const int mixinDeclarationMask = namedMixinApplicationMask << 1;
+
 /// Not a real modifier, and by setting it to zero, it is automatically ignored
 /// by [Modifier.validate] below.
 const int varMask = 0;
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index 620ce91..c7674c6 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -59,6 +59,7 @@
         constMask,
         covariantMask,
         externalMask,
+        mixinDeclarationMask,
         staticMask;
 
 import '../operator.dart'
@@ -535,7 +536,7 @@
     library.addClass(
         documentationComment,
         metadata,
-        abstractMask,
+        mixinDeclarationMask,
         name,
         typeVariables,
         supertype,
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 8df5e4a..97430ab 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
@@ -80,6 +80,8 @@
 
   KernelTypeBuilder mixedInType;
 
+  bool isMixinDeclaration;
+
   SourceClassBuilder(
       List<MetadataBuilder> metadata,
       int modifiers,
@@ -94,8 +96,9 @@
       int startCharOffset,
       int charOffset,
       int charEndOffset,
-      [ShadowClass cls,
-      this.mixedInType])
+      {Class cls,
+      this.mixedInType,
+      this.isMixinDeclaration = false})
       : actualCls = initializeClass(cls, typeVariables, name, parent,
             startCharOffset, charOffset, charEndOffset),
         super(metadata, modifiers, name, typeVariables, supertype, interfaces,
@@ -141,6 +144,7 @@
         supertype?.buildSupertype(library, charOffset, fileUri);
     actualCls.mixedInType =
         mixedInType?.buildMixedInType(library, charOffset, fileUri);
+    actualCls.isMixinDeclaration = isMixinDeclaration;
     // TODO(ahe): If `cls.supertype` is null, and this isn't Object, report a
     // compile-time error.
     cls.isAbstract = isAbstract;
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 9aa8b60..97faf8b 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -291,7 +291,7 @@
   FileOffset fileOffset; // Offset of the name of the class.
   FileOffset fileEndOffset;
   Byte flags (levelBit0, levelBit1, isAbstract, isEnum, isAnonymousMixin,
-              isEliminatedMixin); // Where level is index into ClassLevel
+              isEliminatedMixin, isMixinDeclaration); // Where level is index into ClassLevel
   StringReference name;
   List<Expression> annotations;
   List<TypeParameter> typeParameters;
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 03a709a..90aa8c7 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -728,6 +728,7 @@
   static const int FlagEnum = 1 << 3;
   static const int FlagAnonymousMixin = 1 << 4;
   static const int FlagEliminatedMixin = 1 << 5;
+  static const int FlagMixinDeclaration = 1 << 6;
 
   int flags = 0;
 
@@ -774,6 +775,38 @@
         value ? (flags | FlagEliminatedMixin) : (flags & ~FlagEliminatedMixin);
   }
 
+  /// True if this class was a mixin declaration in Dart.
+  ///
+  /// Mixins are declared in Dart with the `mixin` keyword.  They are compiled
+  /// to Kernel classes.
+  bool get isMixinDeclaration => flags & FlagMixinDeclaration != 0;
+
+  void set isMixinDeclaration(bool value) {
+    flags = value
+        ? (flags | FlagMixinDeclaration)
+        : (flags & ~FlagMixinDeclaration);
+  }
+
+  List<Supertype> superclassConstraints() {
+    var constraints = <Supertype>[];
+
+    // Not a mixin declaration.
+    if (!isMixinDeclaration) return constraints;
+
+    Class previous = this;
+    Class current = supertype.classNode;
+
+    // Otherwise we have a left-linear binary tree (subtrees are supertype and
+    // mixedInType) of constraints, where all the interior nodes are anonymous
+    // mixin applications.
+    while (current != null && current.isAnonymousMixin) {
+      constraints.add(current.mixedInType);
+      previous = current;
+      current = current.supertype.classNode;
+    }
+    return constraints..add(previous.supertype);
+  }
+
   /// The URI of the source file this class was loaded from.
   Uri fileUri;