Infer mixin type arguments.

R=brianwilkerson@google.com

Change-Id: I089bd79c52fc345d1e51045221d02c2342dca0d5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101064
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 51d515f..38d28d2 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -454,6 +454,9 @@
   /// methods might be different.
   int version = 0;
 
+  /// This callback is set during mixins inference to handle reentrant calls.
+  List<InterfaceType> Function(ClassElementImpl) linkedMixinInferenceCallback;
+
   /// Initialize a newly created class element to have the given [name] at the
   /// given [offset] in the file that contains the declaration of this element.
   ClassElementImpl(String name, int offset)
@@ -950,6 +953,10 @@
 
   @override
   List<InterfaceType> get mixins {
+    if (linkedMixinInferenceCallback != null) {
+      _mixins = linkedMixinInferenceCallback(this);
+    }
+
     if (_mixins != null) {
       return _mixins;
     }
diff --git a/pkg/analyzer/lib/src/summary2/default_types_builder.dart b/pkg/analyzer/lib/src/summary2/default_types_builder.dart
index c57a727..18a4fcb3 100644
--- a/pkg/analyzer/lib/src/summary2/default_types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/default_types_builder.dart
@@ -34,6 +34,9 @@
       } else if (node is GenericTypeAlias) {
         _breakRawTypeCycles(node.declaredElement, node.typeParameters);
         _computeBounds(node.typeParameters);
+      } else if (node is MixinDeclaration) {
+        _breakRawTypeCycles(node.declaredElement, node.typeParameters);
+        _computeBounds(node.typeParameters);
       }
     }
     for (var node in nodes) {
@@ -45,6 +48,8 @@
         _build(node.typeParameters);
       } else if (node is GenericTypeAlias) {
         _build(node.typeParameters);
+      } else if (node is MixinDeclaration) {
+        _build(node.typeParameters);
       }
     }
   }
diff --git a/pkg/analyzer/lib/src/summary2/types_builder.dart b/pkg/analyzer/lib/src/summary2/types_builder.dart
index 579bf7fd..0fa8a2b 100644
--- a/pkg/analyzer/lib/src/summary2/types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/types_builder.dart
@@ -42,6 +42,8 @@
       builder.build();
     }
 
+    _MixinsInference(typeSystem).perform(nodes.declarations);
+
     for (var declaration in nodes.declarations) {
       _declaration(declaration);
     }
@@ -79,9 +81,7 @@
     );
   }
 
-  void _classDeclaration(ClassDeclaration node) {
-    _MixinInference(this).perform(node);
-  }
+  void _classDeclaration(ClassDeclaration node) {}
 
   void _declaration(AstNode node) {
     if (node is ClassDeclaration) {
@@ -172,20 +172,17 @@
 
 /// Performs mixins inference in a [ClassDeclaration].
 class _MixinInference {
-  final TypesBuilder builder;
+  final Dart2TypeSystem typeSystem;
 
   InterfaceType classType;
   List<InterfaceType> mixinTypes = [];
   List<InterfaceType> supertypesForMixinInference;
 
-  _MixinInference(this.builder);
+  _MixinInference(this.typeSystem, this.classType);
 
-  void perform(ClassDeclaration node) {
-    var withClause = node.withClause;
+  void perform(WithClause withClause) {
     if (withClause == null) return;
 
-    classType = node.declaredElement.type;
-
     for (var mixinNode in withClause.mixinTypes) {
       var mixinType = _inferSingle(mixinNode);
       mixinTypes.add(mixinType);
@@ -247,8 +244,8 @@
       return mixinType;
     }
 
-    var mixinSupertypeConstraints = builder.typeSystem
-        .gatherMixinSupertypeConstraintsForInference(mixinElement);
+    var mixinSupertypeConstraints =
+        typeSystem.gatherMixinSupertypeConstraintsForInference(mixinElement);
     if (mixinSupertypeConstraints.isEmpty) {
       return mixinType;
     }
@@ -279,7 +276,7 @@
     // Try to pattern match matchingInterfaceTypes against
     // mixinSupertypeConstraints to find the correct set of type
     // parameters to apply to the mixin.
-    var inferredMixin = builder.typeSystem.matchSupertypeConstraints(
+    var inferredMixin = typeSystem.matchSupertypeConstraints(
       mixinElement,
       mixinSupertypeConstraints,
       matchingInterfaceTypes,
@@ -296,6 +293,60 @@
     if (type is InterfaceType && !type.element.isEnum) {
       return type;
     }
-    return builder.typeSystem.typeProvider.objectType;
+    return typeSystem.typeProvider.objectType;
+  }
+}
+
+/// Performs mixin inference for all declarations.
+class _MixinsInference {
+  final Dart2TypeSystem typeSystem;
+
+  _MixinsInference(this.typeSystem);
+
+  void perform(List<AstNode> declarations) {
+    for (var node in declarations) {
+      if (node is ClassDeclaration || node is ClassTypeAlias) {
+        ClassElementImpl element = (node as Declaration).declaredElement;
+        element.linkedMixinInferenceCallback = _callbackWhenRecursion;
+      }
+    }
+
+    for (var declaration in declarations) {
+      _inferDeclaration(declaration);
+    }
+  }
+
+  /// This method is invoked when mixins are asked from the [element], and
+  /// we are inferring the [element] now, i.e. there is a loop.
+  ///
+  /// This is an error. So, we return the empty list, and break the loop.
+  List<InterfaceType> _callbackWhenLoop(ClassElementImpl element) {
+    element.linkedMixinInferenceCallback = null;
+    return <InterfaceType>[];
+  }
+
+  /// This method is invoked when mixins are asked from the [element], and
+  /// we are not inferring the [element] now, i.e. there is no loop.
+  List<InterfaceType> _callbackWhenRecursion(ClassElementImpl element) {
+    _inferDeclaration(element.linkedNode);
+    // The inference was successful, let the element return actual mixins.
+    return null;
+  }
+
+  void _infer(ClassElementImpl element, WithClause withClause) {
+    element.linkedMixinInferenceCallback = _callbackWhenLoop;
+    try {
+      _MixinInference(typeSystem, element.type).perform(withClause);
+    } finally {
+      element.linkedMixinInferenceCallback = null;
+    }
+  }
+
+  void _inferDeclaration(AstNode node) {
+    if (node is ClassDeclaration) {
+      _infer(node.declaredElement, node.withClause);
+    } else if (node is ClassTypeAlias) {
+      _infer(node.declaredElement, node.withClause);
+    }
   }
 }