Index: compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
diff --git a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
index 5ce4099ee2c2f29109d571d22900f96183743212..5cf362e3b62a1e121b624bc2e9bdc1354816ab84 100644 |
--- a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
+++ b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
@@ -111,6 +111,7 @@ import com.google.dart.compiler.resolver.Element; |
import com.google.dart.compiler.resolver.ElementKind; |
import com.google.dart.compiler.resolver.Elements; |
import com.google.dart.compiler.resolver.FieldElement; |
+import com.google.dart.compiler.resolver.FunctionAliasElement; |
import com.google.dart.compiler.resolver.LibraryElement; |
import com.google.dart.compiler.resolver.MethodElement; |
import com.google.dart.compiler.resolver.NodeElement; |
@@ -1398,9 +1399,13 @@ public class TypeAnalyzer implements DartCompilationPhase { |
@Override |
public Type visitFunctionTypeAlias(DartFunctionTypeAlias node) { |
- if (TypeKind.of(node.getElement().getType()).equals(TypeKind.FUNCTION_ALIAS)) { |
- FunctionAliasType type = node.getElement().getType(); |
+ FunctionAliasElement element = node.getElement(); |
+ FunctionAliasType type = element.getType(); |
+ if (TypeKind.of(type) == TypeKind.FUNCTION_ALIAS) { |
checkCyclicBounds(type.getElement().getTypeParameters()); |
+ if (hasFunctionTypeAliasSelfReference(element)) { |
+ onError(node, TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF); |
+ } |
} |
return typeAsVoid(node); |
} |
@@ -2266,6 +2271,64 @@ public class TypeAnalyzer implements DartCompilationPhase { |
return voidType; |
} |
+ /** |
+ * @return <code>true</code> if given {@link FunctionAliasElement} has direct or indirect |
+ * reference to itself using other {@link FunctionAliasElement}s. |
+ */ |
+ private boolean hasFunctionTypeAliasSelfReference(FunctionAliasElement target) { |
+ Set<FunctionAliasElement> visited = Sets.newHashSet(); |
+ return hasFunctionTypeAliasReference(visited, target, target); |
+ } |
+ |
+ /** |
+ * Checks if "target" is referenced by "current". |
+ */ |
+ private boolean hasFunctionTypeAliasReference(Set<FunctionAliasElement> visited, |
+ FunctionAliasElement target, FunctionAliasElement current) { |
+ FunctionType type = current.getFunctionType(); |
+ // prepare Types directly referenced by "current" |
+ Set<Type> referencedTypes = Sets.newHashSet(); |
+ if (type != null) { |
+ // return type |
+ referencedTypes.add(type.getReturnType()); |
+ // parameters |
+ referencedTypes.addAll(type.getParameterTypes()); |
+ referencedTypes.addAll(type.getNamedParameterTypes().values()); |
+ } |
+ // check that referenced types do not have references on "target" |
+ for (Type referencedType : referencedTypes) { |
+ if (referencedType != null |
+ && hasFunctionTypeAliasReference(visited, target, referencedType.getElement())) { |
+ return true; |
+ } |
+ } |
+ // no |
+ return false; |
+ } |
+ |
+ /** |
+ * Checks if "target" is referenced by "current". |
+ */ |
+ private boolean hasFunctionTypeAliasReference(Set<FunctionAliasElement> visited, |
+ FunctionAliasElement target, Element currentElement) { |
+ // only if "current" in function type alias |
+ if (!(currentElement instanceof FunctionAliasElement)) { |
+ return false; |
+ } |
+ FunctionAliasElement current = (FunctionAliasElement) currentElement; |
+ // found "target" |
+ if (Objects.equal(target, current)) { |
+ return true; |
+ } |
+ // prevent recursion |
+ if (visited.contains(current)) { |
+ return false; |
+ } |
+ visited.add(current); |
+ // check type of "current" function type alias |
+ return hasFunctionTypeAliasReference(visited, target, current); |
+ } |
+ |
@Override |
public Type visitIntegerLiteral(DartIntegerLiteral node) { |
return typeOfLiteral(node); |