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);
+ }
}
}