| 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) {
|
|
|