Index: pkg/compiler/lib/src/resolution/constructors.dart |
diff --git a/pkg/compiler/lib/src/resolution/constructors.dart b/pkg/compiler/lib/src/resolution/constructors.dart |
index cff366c5327ee36d067c8a8e54f20122f607e92c..66af813e4b797cda5276a41ed0620619af899223 100644 |
--- a/pkg/compiler/lib/src/resolution/constructors.dart |
+++ b/pkg/compiler/lib/src/resolution/constructors.dart |
@@ -415,10 +415,9 @@ class InitializerResolver { |
} |
} |
-class ConstructorResolver extends CommonResolverVisitor<Element> { |
+class ConstructorResolver extends CommonResolverVisitor<ConstructorResult> { |
final ResolverVisitor resolver; |
- bool inConstContext; |
- DartType type; |
+ final bool inConstContext; |
ConstructorResolver(Compiler compiler, this.resolver, |
{bool this.inConstContext: false}) |
@@ -430,8 +429,10 @@ class ConstructorResolver extends CommonResolverVisitor<Element> { |
throw 'not supported'; |
} |
- ErroneousConstructorElementX failOrReturnErroneousConstructorElement( |
+ ConstructorResult reportAndCreateErroneousConstructorElement( |
Spannable diagnosticNode, |
+ ConstructorResultKind resultKind, |
+ DartType type, |
Element enclosing, |
String name, |
MessageKind kind, |
@@ -448,171 +449,281 @@ class ConstructorResolver extends CommonResolverVisitor<Element> { |
} else { |
compiler.reportWarning(diagnosticNode, kind, arguments); |
} |
- return new ErroneousConstructorElementX( |
+ ErroneousElement error = new ErroneousConstructorElementX( |
kind, arguments, name, enclosing); |
+ if (type == null) { |
+ type = new MalformedType(error, null); |
+ } |
+ return new ConstructorResult(resultKind, error, type); |
} |
- FunctionElement resolveConstructor(ClassElement cls, |
- Node diagnosticNode, |
- String constructorName) { |
+ ConstructorResult resolveConstructor( |
+ InterfaceType type, |
+ Node diagnosticNode, |
+ String constructorName) { |
+ ClassElement cls = type.element; |
cls.ensureResolved(compiler); |
- Element result = cls.lookupConstructor(constructorName); |
+ ConstructorElement constructor = cls.lookupConstructor(constructorName); |
// TODO(johnniwinther): Use [Name] for lookup. |
if (Name.isPrivateName(constructorName) && |
resolver.enclosingElement.library != cls.library) { |
- result = null; |
+ constructor = null; |
} |
- if (result == null) { |
- String fullConstructorName = Elements.constructorNameForDiagnostics( |
- cls.name, |
- constructorName); |
- return failOrReturnErroneousConstructorElement( |
+ if (constructor == null) { |
+ String fullConstructorName = |
+ Elements.constructorNameForDiagnostics(cls.name, constructorName); |
+ return reportAndCreateErroneousConstructorElement( |
diagnosticNode, |
+ ConstructorResultKind.UNRESOLVED_CONSTRUCTOR, type, |
cls, constructorName, |
MessageKind.CANNOT_FIND_CONSTRUCTOR, |
{'constructorName': fullConstructorName}, |
missingConstructor: true); |
- } else if (inConstContext && !result.isConst) { |
- error(diagnosticNode, MessageKind.CONSTRUCTOR_IS_NOT_CONST); |
+ } else if (inConstContext && !constructor.isConst) { |
+ compiler.reportError( |
+ diagnosticNode, MessageKind.CONSTRUCTOR_IS_NOT_CONST); |
+ return new ConstructorResult( |
+ ConstructorResultKind.NON_CONSTANT, constructor, type); |
+ } else { |
+ if (constructor.isGenerativeConstructor) { |
+ if (cls.isAbstract) { |
+ compiler.reportWarning( |
+ diagnosticNode, MessageKind.ABSTRACT_CLASS_INSTANTIATION); |
+ registry.registerAbstractClassInstantiation(); |
+ return new ConstructorResult( |
+ ConstructorResultKind.ABSTRACT, constructor, type); |
+ } else { |
+ return new ConstructorResult( |
+ ConstructorResultKind.GENERATIVE, constructor, type); |
+ } |
+ } else { |
+ assert(invariant(diagnosticNode, constructor.isFactoryConstructor, |
+ message: "Unexpected constructor $constructor.")); |
+ return new ConstructorResult( |
+ ConstructorResultKind.FACTORY, constructor, type); |
+ } |
} |
- return result; |
} |
- Element visitNewExpression(NewExpression node) { |
- inConstContext = node.isConst; |
+ ConstructorResult visitNewExpression(NewExpression node) { |
Node selector = node.send.selector; |
- Element element = visit(selector); |
- assert(invariant(selector, element != null, |
- message: 'No element return for $selector.')); |
- return finishConstructorReference(element, node.send.selector, node); |
+ ConstructorResult result = visit(selector); |
+ assert(invariant(selector, result != null, |
+ message: 'No result returned for $selector.')); |
+ return finishConstructorReference(result, node.send.selector, node); |
} |
/// Finishes resolution of a constructor reference and records the |
/// type of the constructed instance on [expression]. |
- FunctionElement finishConstructorReference(Element element, |
- Node diagnosticNode, |
- Node expression) { |
- assert(invariant(diagnosticNode, element != null, |
- message: 'No element return for $diagnosticNode.')); |
+ ConstructorResult finishConstructorReference( |
+ ConstructorResult result, |
+ Node diagnosticNode, |
+ Node expression) { |
+ assert(invariant(diagnosticNode, result != null, |
+ message: 'No result returned for $diagnosticNode.')); |
+ |
+ if (result.kind != null) { |
+ resolver.registry.setType(expression, result.type); |
+ return result; |
+ } |
+ |
// Find the unnamed constructor if the reference resolved to a |
// class. |
- if (!Elements.isUnresolved(element) && !element.isConstructor) { |
- if (element.isClass) { |
- ClassElement cls = element; |
- cls.ensureResolved(compiler); |
- // The unnamed constructor may not exist, so [e] may become unresolved. |
- element = resolveConstructor(cls, diagnosticNode, ''); |
+ if (result.type != null) { |
+ // The unnamed constructor may not exist, so [e] may become unresolved. |
+ result = resolveConstructor(result.type, diagnosticNode, ''); |
+ } else { |
+ Element element = result.element; |
+ if (element.isErroneous) { |
+ result = constructorResultForErroneous(diagnosticNode, element); |
} else { |
- element = failOrReturnErroneousConstructorElement( |
+ result = reportAndCreateErroneousConstructorElement( |
diagnosticNode, |
+ ConstructorResultKind.INVALID_TYPE, null, |
element, element.name, |
MessageKind.NOT_A_TYPE, {'node': diagnosticNode}); |
} |
- } else if (element.isErroneous && element is! ErroneousElementX) { |
- // Parser error. The error has already been reported. |
- element = new ErroneousConstructorElementX( |
- MessageKind.NOT_A_TYPE, {'node': diagnosticNode}, |
- element.name, element); |
- registry.registerThrowRuntimeError(); |
- } |
- |
- if (type == null) { |
- if (Elements.isUnresolved(element)) { |
- type = const DynamicType(); |
- } else { |
- type = element.enclosingClass.rawType; |
- } |
} |
- resolver.registry.setType(expression, type); |
- return element; |
+ resolver.registry.setType(expression, result.type); |
+ return result; |
} |
- Element visitTypeAnnotation(TypeAnnotation node) { |
- assert(invariant(node, type == null)); |
+ ConstructorResult visitTypeAnnotation(TypeAnnotation node) { |
// This is not really resolving a type-annotation, but the name of the |
// constructor. Therefore we allow deferred types. |
- type = resolver.resolveTypeAnnotation(node, |
- malformedIsError: inConstContext, |
- deferredIsMalformed: false); |
+ DartType type = resolver.resolveTypeAnnotation( |
+ node, |
+ malformedIsError: inConstContext, |
+ deferredIsMalformed: false); |
registry.registerRequiredType(type, resolver.enclosingElement); |
- return type.element; |
+ return constructorResultForType(node, type); |
} |
- Element visitSend(Send node) { |
- Element element = visit(node.receiver); |
- assert(invariant(node.receiver, element != null, |
- message: 'No element return for $node.receiver.')); |
- if (Elements.isUnresolved(element)) return element; |
+ ConstructorResult visitSend(Send node) { |
+ ConstructorResult receiver = visit(node.receiver); |
+ assert(invariant(node.receiver, receiver != null, |
+ message: 'No result returned for $node.receiver.')); |
+ if (receiver.kind != null) { |
+ assert(invariant(node, receiver.element.isErroneous, |
+ message: "Unexpected prefix result: $receiver.")); |
+ // We have already found an error. |
+ return receiver; |
+ } |
+ |
Identifier name = node.selector.asIdentifier(); |
if (name == null) internalError(node.selector, 'unexpected node'); |
- if (element.isClass) { |
- ClassElement cls = element; |
- cls.ensureResolved(compiler); |
- return resolveConstructor(cls, name, name.source); |
- } else if (element.isPrefix) { |
- PrefixElement prefix = element; |
- element = prefix.lookupLocalMember(name.source); |
- element = Elements.unwrap(element, compiler, node); |
- if (element == null) { |
- return failOrReturnErroneousConstructorElement( |
- name, |
- resolver.enclosingElement, name.source, |
- MessageKind.CANNOT_RESOLVE, {'name': name}); |
- } else if (!element.isClass) { |
- return failOrReturnErroneousConstructorElement( |
+ if (receiver.type != null) { |
+ if (receiver.type.isInterfaceType) { |
+ return resolveConstructor(receiver.type, name, name.source); |
+ } else { |
+ // TODO(johnniwinther): Update the message for the different types. |
+ return reportAndCreateErroneousConstructorElement( |
name, |
+ ConstructorResultKind.INVALID_TYPE, null, |
resolver.enclosingElement, name.source, |
- MessageKind.NOT_A_TYPE, {'node': name}, |
- isError: true); |
+ MessageKind.NOT_A_TYPE, {'node': name}); |
} |
+ } else if (receiver.element.isPrefix) { |
+ PrefixElement prefix = receiver.element; |
+ Element member = prefix.lookupLocalMember(name.source); |
+ return constructorResultForElement(node, name.source, member); |
} else { |
- internalError(node.receiver, 'unexpected element $element'); |
+ return internalError(node.receiver, 'unexpected receiver $receiver'); |
} |
- return element; |
} |
- Element visitIdentifier(Identifier node) { |
+ ConstructorResult visitIdentifier(Identifier node) { |
String name = node.source; |
Element element = resolver.reportLookupErrorIfAny( |
lookupInScope(compiler, node, resolver.scope, name), node, name); |
registry.useElement(node, element); |
// TODO(johnniwinther): Change errors to warnings, cf. 11.11.1. |
+ return constructorResultForElement(node, name, element); |
+ } |
+ |
+ /// Assumed to be called by [resolveRedirectingFactory]. |
+ ConstructorResult visitRedirectingFactoryBody(RedirectingFactoryBody node) { |
+ Node constructorReference = node.constructorReference; |
+ return finishConstructorReference(visit(constructorReference), |
+ constructorReference, node); |
+ } |
+ |
+ ConstructorResult constructorResultForElement( |
+ Node node, String name, Element element) { |
+ element = Elements.unwrap(element, compiler, node); |
if (element == null) { |
- return failOrReturnErroneousConstructorElement( |
+ return reportAndCreateErroneousConstructorElement( |
node, |
+ ConstructorResultKind.INVALID_TYPE, null, |
resolver.enclosingElement, name, |
MessageKind.CANNOT_RESOLVE, |
{'name': name}); |
} else if (element.isErroneous) { |
- return element; |
+ return constructorResultForErroneous(node, element); |
+ } else if (element.isClass) { |
+ ClassElement cls = element; |
+ cls.computeType(compiler); |
+ return constructorResultForType(node, cls.rawType); |
+ } else if (element.isPrefix) { |
+ return new ConstructorResult.forElement(element); |
} else if (element.isTypedef) { |
- element = failOrReturnErroneousConstructorElement( |
+ TypedefElement typdef = element; |
+ typdef.ensureResolved(compiler); |
+ return constructorResultForType(node, typdef.rawType); |
+ } else if (element.isTypeVariable) { |
+ TypeVariableElement typeVariableElement = element; |
+ return constructorResultForType(node, typeVariableElement.type); |
+ } else { |
+ return reportAndCreateErroneousConstructorElement( |
node, |
+ ConstructorResultKind.INVALID_TYPE, null, |
resolver.enclosingElement, name, |
- MessageKind.CANNOT_INSTANTIATE_TYPEDEF, {'typedefName': name}, |
- isError: true); |
- } else if (element.isTypeVariable) { |
- element = failOrReturnErroneousConstructorElement( |
+ MessageKind.NOT_A_TYPE, {'node': name}); |
+ } |
+ } |
+ |
+ ConstructorResult constructorResultForErroneous( |
+ Node node, Element error) { |
+ if (error is! ErroneousElementX) { |
+ // Parser error. The error has already been reported. |
+ error = new ErroneousConstructorElementX( |
+ MessageKind.NOT_A_TYPE, {'node': node}, |
+ error.name, error); |
+ registry.registerThrowRuntimeError(); |
+ } |
+ return new ConstructorResult( |
+ ConstructorResultKind.INVALID_TYPE, |
+ error, |
+ new MalformedType(error, null)); |
+ } |
+ |
+ ConstructorResult constructorResultForType( |
+ Node node, |
+ DartType type) { |
+ String name = type.name; |
+ if (type.isMalformed) { |
+ return new ConstructorResult( |
+ ConstructorResultKind.INVALID_TYPE, type.element, type); |
+ } else if (type.isInterfaceType) { |
+ return new ConstructorResult.forType(type); |
+ } else if (type.isTypedef) { |
+ return reportAndCreateErroneousConstructorElement( |
node, |
+ ConstructorResultKind.INVALID_TYPE, type, |
resolver.enclosingElement, name, |
- MessageKind.CANNOT_INSTANTIATE_TYPE_VARIABLE, |
- {'typeVariableName': name}, |
- isError: true); |
- } else if (!element.isClass && !element.isPrefix) { |
- element = failOrReturnErroneousConstructorElement( |
+ MessageKind.CANNOT_INSTANTIATE_TYPEDEF, {'typedefName': name}); |
+ } else if (type.isTypeVariable) { |
+ return reportAndCreateErroneousConstructorElement( |
node, |
+ ConstructorResultKind.INVALID_TYPE, type, |
resolver.enclosingElement, name, |
- MessageKind.NOT_A_TYPE, {'node': name}, |
- isError: true); |
+ MessageKind.CANNOT_INSTANTIATE_TYPE_VARIABLE, |
+ {'typeVariableName': name}); |
} |
- return element; |
+ internalError(node, "Unexpected constructor type $type"); |
+ return null; |
} |
- /// Assumed to be called by [resolveRedirectingFactory]. |
- Element visitRedirectingFactoryBody(RedirectingFactoryBody node) { |
- Node constructorReference = node.constructorReference; |
- return finishConstructorReference(visit(constructorReference), |
- constructorReference, node); |
+} |
+ |
+enum ConstructorResultKind { |
+ GENERATIVE, |
+ FACTORY, |
+ ABSTRACT, |
+ INVALID_TYPE, |
+ UNRESOLVED_CONSTRUCTOR, |
+ NON_CONSTANT, |
+} |
+ |
+class ConstructorResult { |
+ final ConstructorResultKind kind; |
+ final Element element; |
+ final DartType type; |
+ |
+ ConstructorResult(this.kind, this.element, this.type); |
+ |
+ ConstructorResult.forElement(this.element) |
+ : kind = null, |
+ type = null; |
+ |
+ ConstructorResult.forType(this.type) |
+ : kind = null, |
+ element = null; |
+ |
+ String toString() { |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write('ConstructorResult('); |
+ if (kind != null) { |
+ sb.write('kind=$kind,'); |
+ sb.write('element=$element,'); |
+ sb.write('type=$type'); |
+ } else if (element != null) { |
+ sb.write('element=$element'); |
+ } else { |
+ sb.write('type=$type'); |
+ } |
+ sb.write(')'); |
+ return sb.toString(); |
} |
} |