Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Unified Diff: lib/compiler/implementation/typechecker.dart

Issue 10826045: Substitution handled for most Send nodes. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/compiler/implementation/tree/nodes.dart ('k') | lib/compiler/implementation/warnings.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/compiler/implementation/typechecker.dart
diff --git a/lib/compiler/implementation/typechecker.dart b/lib/compiler/implementation/typechecker.dart
index 440c60b7f974d75817b8d7a0a1af6f96da027bef..f2716d423262fc4a763e95529d66d4943bf596f7 100644
--- a/lib/compiler/implementation/typechecker.dart
+++ b/lib/compiler/implementation/typechecker.dart
@@ -29,13 +29,16 @@ interface Type {
Element get element();
/**
- * Performs the substitution [arguments[i]/parameters[i]]this.
+ * Performs the substitution `[arguments[i]/parameters[i]]this`, where
+ * `parameters[i]` denotes the i'th type variable in [typeParameters].
* The notation is known from this lambda calculus rule:
- * (lambda x.e0)e1 -> [e1/x]e0.
+ *
+ * (lambda x.e0)e1 -> [e1/x]e0.
*
* See [TypeVariableType] for a motivation for this method.
*/
- Type subst(Link<Type> arguments, List<Type> parameters);
+ Type subst(Compiler compiler, Link<Type> arguments,
+ LinkedHashMap<SourceString, TypeVariableElement> typeParameters);
}
/**
@@ -65,20 +68,33 @@ class TypeVariableType implements Type {
TypeVariableElement element;
TypeVariableType(this.name, [this.element]);
- Type subst(Link<Type> arguments, List<Type> parameters) {
- if (arguments.isEmpty()) {
+ Type subst(Compiler compiler, Link<Type> arguments,
+ LinkedHashMap<SourceString, TypeVariableElement> typeParameters) {
+ if (typeParameters.isEmpty()) {
// Return fast on empty substitutions.
return this;
}
- Link<Type> argumentLink = arguments;
- Iterator<Type> parameterIterator = parameters.iterator();
- while (!argumentLink.isEmpty() && parameterIterator.hasNext()) {
- Type parameter = parameterIterator.next();
- Type argument = argumentLink.head;
- if (parameter === this) {
- return argument;
+ Iterator<TypeVariableElement> parameterIterator =
+ typeParameters.getValues().iterator();
+ if (arguments.isEmpty()) {
+ // Raw substitution: Dynamic replaces parameters.
+ while (parameterIterator.hasNext()) {
+ TypeVariableType parameter = parameterIterator.next().type;
+ if (parameter == this) {
+ return compiler.types.dynamicType;
+ }
+ }
+ } else {
+ // Normal substitution.
+ Link<Type> argumentLink = arguments;
+ while (!argumentLink.isEmpty() && parameterIterator.hasNext()) {
+ TypeVariableType parameter = parameterIterator.next().type;
+ Type argument = argumentLink.head;
+ if (parameter == this) {
+ return argument;
+ }
+ argumentLink = argumentLink.tail;
}
- argumentLink = argumentLink.tail;
}
// The type variable was not substituted.
return this;
@@ -107,7 +123,8 @@ class StatementType implements Type {
return (this === other) ? this : MAYBE_RETURNING;
}
- Type subst(Link<Type> arguments, List<Type> parameters) {
+ Type subst(Compiler compiler, Link<Type> arguments,
+ LinkedHashMap<SourceString, TypeVariableElement> typeParameters) {
// Statement types are not substitutable.
return this;
}
@@ -120,7 +137,8 @@ class VoidType implements Type {
SourceString get name() => element.name;
final VoidElement element;
- Type subst(Link<Type> arguments, List<Type> parameters) {
+ Type subst(Compiler compiler, Link<Type> arguments,
+ LinkedHashMap<SourceString, TypeVariableElement> typeParameters) {
// Void cannot be substituted.
return this;
}
@@ -129,33 +147,39 @@ class VoidType implements Type {
}
class InterfaceType implements Type {
- final Element element;
- final Link<Type> arguments;
+ final ClassElement element;
+ final Link<Type> typeArguments;
const InterfaceType(this.element,
- [this.arguments = const EmptyLink<Type>()]);
+ [this.typeArguments = const EmptyLink<Type>()]);
SourceString get name() => element.name;
- Type subst(Link<Type> replacements, List<Type> parameters) {
- if (arguments.isEmpty()) {
+ Type subst(Compiler compiler, Link<Type> arguments,
+ LinkedHashMap<SourceString, TypeVariableElement> typeParameters) {
+ if (typeArguments.isEmpty()) {
// Return fast on non-generic types.
return this;
}
- if (replacements.isEmpty()) {
+ if (typeParameters.isEmpty()) {
// Return fast on empty substitutions.
return this;
}
+ if (arguments.isEmpty()) {
+ // Return the 'raw' type on raw substitutions.
+ return new InterfaceType(element);
+ }
bool changed = false;
var argumentsBuilder = new LinkBuilder<Type>();
- Link<Type> argument = arguments;
- while (!argument.isEmpty()) {
- var replacement = argument.head.subst(replacements, parameters);
- if (!changed && replacement !== argument.head) {
+ Link<Type> typeArgument = typeArguments;
+ while (!typeArgument.isEmpty()) {
+ var argument =
+ typeArgument.head.subst(compiler, arguments, typeParameters);
+ if (!changed && argument !== typeArgument.head) {
changed = true;
}
- argumentsBuilder.addLast(replacement);
- argument = argument.tail;
+ argumentsBuilder.addLast(argument);
+ typeArgument = typeArgument.tail;
}
if (changed) {
// Create a new type only if necessary.
@@ -164,12 +188,41 @@ class InterfaceType implements Type {
return this;
}
+ /**
+ * Finds the method, field or property on this interface type named [name].
+ */
+ Member lookupMember(Compiler compiler, SourceString name) {
+ ClassElement classElement = element;
+ InterfaceType receiver = this;
+ InterfaceType declarer = receiver;
+ Element member = classElement.lookupLocalMember(name);
+ if (member === null) {
+ classElement.ensureResolved(compiler);
+ for (Link<InterfaceType> supertypes = classElement.allSupertypes;
+ !supertypes.isEmpty() && member === null;
+ supertypes = supertypes.tail) {
+ declarer = supertypes.head;
+ ClassElement lookupTarget = declarer.element;
+ member = lookupTarget.lookupLocalMember(name);
+ }
+ }
+ if (member == null) {
+ return null;
+ }
+ if (member.kind == ElementKind.FUNCTION ||
+ member.kind == ElementKind.ABSTRACT_FIELD ||
+ member.kind == ElementKind.FIELD) {
+ return new Member(receiver, declarer, member);
+ }
+ return null;
+ }
+
String toString() {
StringBuffer sb = new StringBuffer();
sb.add(name.slowToString());
- if (!arguments.isEmpty()) {
+ if (!typeArguments.isEmpty()) {
sb.add('<');
- arguments.printOn(sb, ', ');
+ typeArguments.printOn(sb, ', ');
sb.add('>');
}
return sb.toString();
@@ -184,26 +237,28 @@ class FunctionType implements Type {
const FunctionType(Type this.returnType, Link<Type> this.parameterTypes,
Element this.element);
- Type subst(Link<Type> arguments, List<Type> parameters) {
- if (arguments.isEmpty()) {
+ Type subst(Compiler compiler, Link<Type> arguments,
+ LinkedHashMap<SourceString, TypeVariableElement> typeParameters) {
+ if (typeParameters.isEmpty()) {
// Return fast on empty substitutions.
return this;
}
- var newReturnType = returnType.subst(arguments, parameters);
+ var newReturnType = returnType.subst(compiler, arguments, typeParameters);
bool changed = newReturnType !== returnType;
- var parameterBuilder = new LinkBuilder<Type>();
+ var newParameterTypesBuilder = new LinkBuilder<Type>();
Link<Type> parameterType = parameterTypes;
while (!parameterType.isEmpty()) {
- var replacement = parameterType.head.subst(arguments, parameters);
- if (!changed && replacement !== parameterType.head) {
+ var newParameterType =
+ parameterType.head.subst(compiler, arguments, typeParameters);
+ if (!changed && newParameterType !== parameterType.head) {
changed = true;
}
- parameterBuilder.addLast(replacement);
+ newParameterTypesBuilder.addLast(newParameterType);
parameterType = parameterType.tail;
}
if (changed) {
// Create a new type only if necessary.
- return new FunctionType(newReturnType, parameterBuilder.toLink(),
+ return new FunctionType(newReturnType, newParameterTypesBuilder.toLink(),
element);
}
return this;
@@ -227,15 +282,74 @@ class FunctionType implements Type {
}
}
+/**
+ * Member encapsulates a member (constructor, method, field, property, getter,
+ * or setter) with the types of the declarer and receiver in order to do
+ * substitution on the member type.
+ *
+ * Consider for instance these classes and the variable `B<String> b`:
+ *
+ * class A<E> {
+ * E field;
+ * }
+ * class B<F> extends A<F> {}
+ *
+ * In a [Member] for `b.field` the [receiver] is `B<String>` and the declarer
+ * is `A<F>`, which is the supertype of `B<F>` from which `field` has been
+ * inherited. To compute the type of `b.field` we must first substitute `E`
+ * by `F` using the relation between `A<E>` and `A<F>`, and then `F` by `String`
+ * using the relation between `B<F>` and `B<String>`.
+ */
+class Member {
+ final InterfaceType receiver;
+ final InterfaceType declarer;
+ final Element element;
+
+ Member(this.receiver, this.declarer, this.element);
+
+ Type computeType(Compiler compiler) {
+ Type type;
+ if (element.kind == ElementKind.ABSTRACT_FIELD) {
+ AbstractFieldElement abstractFieldElement = element;
+ if (abstractFieldElement.getter != null) {
+ type = abstractFieldElement.getter.computeType(compiler).returnType;
+ } else {
+ type = abstractFieldElement.setter.computeType(
+ compiler).parameterTypes.head;
+ if (type == null) {
+ type = compiler.types.dynamicType;
+ }
+ }
+ } else {
+ type = element.computeType(compiler);
+ }
+ if (!declarer.element.typeParameters.isEmpty()) {
+ type = type.subst(compiler,
+ declarer.typeArguments, declarer.element.typeParameters);
+ type = type.subst(compiler,
+ receiver.typeArguments, receiver.element.typeParameters);
+ }
+ return type;
+ }
+
+ String toString() {
+ return '$receiver.$element';
+ }
+}
+
class Types {
final VoidType voidType;
final InterfaceType dynamicType;
+ final Element functionElement;
- Types(Element dynamicElement)
- : this.with(dynamicElement, new LibraryElement(new Script(null, null)));
+ Types(Element dynamicElement, Element functionElement)
+ : this.with(dynamicElement, functionElement,
+ new LibraryElement(new Script(null, null)));
// TODO(karlklose): should we have a class Void?
- Types.with(Element dynamicElement, LibraryElement library)
+ Types.with(Element dynamicElement,
+ Element this.functionElement,
+ LibraryElement library)
: voidType = new VoidType(new VoidElement(library)),
dynamicType = new InterfaceType(dynamicElement);
@@ -258,6 +372,7 @@ class Types {
}
return false;
} else if (t is FunctionType) {
+ if (s.element == functionElement) return true;
if (s is !FunctionType) return false;
FunctionType tf = t;
FunctionType sf = s;
@@ -318,9 +433,12 @@ class TypeCheckerVisitor implements Visitor<Type> {
listType = compiler.listClass.computeType(compiler);
}
- Type fail(node, [reason]) {
+ Type fail(Node node, [reason]) {
String message = 'cannot type-check';
- if (reason !== null) {
+ if (node != null) {
+ message = '$message ${node.getObjectDescription()} `$node`';
+ }
+ if (reason != null) {
message = '$message: $reason';
}
throw new CancelTypeCheckException(node, message);
@@ -506,6 +624,37 @@ class TypeCheckerVisitor implements Visitor<Type> {
return types.dynamicType;
}
+ /**
+ * Returns the interface type on which to lookup members. If [type] is the
+ * function of a getter, the return type is returned. If [type] is a type
+ * variable the bound is returned.
+ */
+ InterfaceType findReceiverType(Node receiver, Type receiverType) {
+ if (receiverType === null) {
+ fail(receiver, 'receivertype is null');
+ }
+ if (receiverType.element == compiler.dynamicClass) {
+ return receiverType;
+ }
+ ElementKind receiverKind = receiverType.element.kind;
+ if (receiverKind === ElementKind.GETTER) {
+ FunctionType getterType = receiverType;
+ return findReceiverType(receiver, getterType.returnType);
+ }
+ if (receiverKind === ElementKind.TYPEDEF) {
+ // TODO(karlklose): handle typedefs.
+ return null;
+ }
+ if (receiverKind === ElementKind.TYPE_VARIABLE) {
+ TypeVariableElement typeVariableElement = receiverType.element;
+ return findReceiverType(receiver, typeVariableElement.bound);
+ }
+ if (receiverKind !== ElementKind.CLASS) {
+ fail(receiver, 'unexpected receiver kind: ${receiverKind}');
+ }
+ return receiverType;
+ }
+
void analyzeArguments(Send send, FunctionType funType) {
Link<Node> arguments = send.arguments;
if (funType === null || funType === types.dynamicType) {
@@ -571,56 +720,87 @@ class TypeCheckerVisitor implements Visitor<Type> {
} else if (node.isPropertyAccess) {
if (node.receiver !== null) {
- // TODO(karlklose): we cannot handle fields.
- return unhandledExpression();
- }
- Element element = elements[node];
- if (element === null) return types.dynamicType;
- return computeType(element);
+ InterfaceType receiverType =
+ findReceiverType(node.receiver, analyze(node.receiver));
+ if (receiverType === null) return null;
+ if (receiverType.element === compiler.dynamicClass) {
+ return types.dynamicType;
+ }
+ Member member = receiverType.lookupMember(compiler, selector.source);
+ if (member === null ||
+ !(member.element.kind === ElementKind.FIELD ||
+ member.element.kind === ElementKind.ABSTRACT_FIELD)) {
+ reportTypeWarning(selector, MessageKind.PROPERTY_NOT_FOUND,
+ [receiverType, selector.source]);
+ } else {
+ return member.computeType(compiler);
+ }
+ return types.dynamicType;
+ } else {
+ Element element = elements[node];
+ if (element === null) {
+ return types.dynamicType;
+ }
+ if ((element.kind == ElementKind.FIELD ||
+ element.kind == ElementKind.GETTER) &&
+ element.isInstanceMember()) {
+ // Ensure substitution of declared type variables with inherited type
+ // variables.
+ InterfaceType thisType = currentClass.computeType(compiler);
+ Member member = thisType.lookupMember(compiler, selector.source);
+ if (member != null) {
+ // Should always work!
+ return member.computeType(compiler);
+ }
+ compiler.internalError('Could not lookup resolved member '
+ '${selector.source} on ${thisType}.');
+ }
+ return computeType(element);
+ }
} else if (node.isFunctionObjectInvocation) {
fail(node.receiver, 'function object invocation unimplemented');
} else {
FunctionType computeFunType() {
if (node.receiver !== null) {
- Type receiverType = analyze(node.receiver);
- if (receiverType.element == compiler.dynamicClass) return null;
- if (receiverType === null) {
- fail(node.receiver, 'receivertype is null');
- }
- if (receiverType.element.kind === ElementKind.GETTER) {
- FunctionType getterType = receiverType;
- receiverType = getterType.returnType;
- }
- ElementKind receiverKind = receiverType.element.kind;
- if (receiverKind === ElementKind.TYPEDEF) {
- // TODO(karlklose): handle typedefs.
+ InterfaceType receiverType =
+ findReceiverType(node.receiver, analyze(node.receiver));
+ if (receiverType === null) return null;
+ if (receiverType.element === compiler.dynamicClass) return null;
+
+ Member member = receiverType.lookupMember(compiler, selector.source);
+ if (member === null || member.element.kind !== ElementKind.FUNCTION) {
+ reportTypeWarning(selector, MessageKind.METHOD_NOT_FOUND,
+ [receiverType, name]);
return null;
}
- if (receiverKind === ElementKind.TYPE_VARIABLE) {
- // TODO(karlklose): handle type variables.
- return null;
- }
- if (receiverKind !== ElementKind.CLASS) {
- fail(node.receiver, 'unexpected receiver kind: ${receiverKind}');
- }
- ClassElement classElement = receiverType.element;
- // TODO(karlklose): substitute type arguments.
- Type memberType =
- lookupMethodType(selector, classElement, selector.source);
- if (memberType.element === compiler.dynamicClass) return null;
+ Type memberType = member.computeType(compiler);
+ if (memberType.element == compiler.dynamicClass) return null;
+ if (memberType.element == compiler.functionClass) return null;
return memberType;
} else {
Element element = elements[node];
if (element === null) {
fail(node, 'unresolved ${node.selector}');
} else if (element.kind === ElementKind.FUNCTION) {
+ if (element.isInstanceMember()) {
+ // Ensure substitution of declared type variables with inherited
+ // type variables.
+ InterfaceType thisType = currentClass.computeType(compiler);
+ Member member = thisType.lookupMember(compiler, selector.source);
+ if (member != null) {
+ // Should always work!
+ return member.computeType(compiler);
+ }
+ compiler.internalError('Could not lookup resolved member '
+ '${selector.source} on ${thisType}.');
+ }
return computeType(element);
} else if (element.kind === ElementKind.FOREIGN) {
return null;
- } else if (element.kind === ElementKind.VARIABLE
- || element.kind === ElementKind.FIELD) {
+ } else if (element.kind === ElementKind.VARIABLE ||
+ element.kind === ElementKind.FIELD) {
// TODO(karlklose): handle object invocations.
return null;
} else {
@@ -677,13 +857,22 @@ class TypeCheckerVisitor implements Visitor<Type> {
}
Type visitNewExpression(NewExpression node) {
+ InterfaceType type = elements.getType(node.getTypeAnnotation());
Element element = elements[node.send];
- analyzeArguments(node.send, computeType(element));
- return analyze(node.send.selector);
+ var member = new Member(type,
+ element.enclosingElement.computeType(compiler),
+ element);
+ analyzeArguments(node.send, member.computeType(compiler));
+ return type;
}
Type visitLiteralList(LiteralList node) {
- return listType;
+ Type elementType = node.type != null
+ ? elements[node.type]
+ : compiler.types.dynamicType;
+ var linkBuilder = new LinkBuilder<Type>();
+ linkBuilder.addLast(elementType);
+ return new InterfaceType(listType.element, linkBuilder.toLink());
}
Type visitNodeList(NodeList node) {
« no previous file with comments | « lib/compiler/implementation/tree/nodes.dart ('k') | lib/compiler/implementation/warnings.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698