| Index: sdk/lib/_internal/compiler/implementation/typechecker.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/typechecker.dart (revision 22847)
|
| +++ sdk/lib/_internal/compiler/implementation/typechecker.dart (working copy)
|
| @@ -18,22 +18,6 @@
|
| }
|
|
|
| /**
|
| - * Class used to report different warnings for differrent kinds of members.
|
| - */
|
| -class MemberKind {
|
| - static const MemberKind METHOD = const MemberKind("method");
|
| - static const MemberKind OPERATOR = const MemberKind("operator");
|
| - static const MemberKind PROPERTY = const MemberKind("property");
|
| -
|
| - final String name;
|
| -
|
| - const MemberKind(this.name);
|
| -
|
| - String toString() => name;
|
| -}
|
| -
|
| -
|
| -/**
|
| * [ElementAccess] represents the access of [element], either as a property
|
| * access or invocation.
|
| */
|
| @@ -65,8 +49,6 @@
|
| Element get element => member.element;
|
|
|
| DartType computeType(Compiler compiler) => member.computeType(compiler);
|
| -
|
| - String toString() => 'MemberAccess($member)';
|
| }
|
|
|
| /// An access of an unresolved element.
|
| @@ -78,8 +60,6 @@
|
| DartType computeType(Compiler compiler) => compiler.types.dynamicType;
|
|
|
| bool isCallable(Compiler compiler) => true;
|
| -
|
| - String toString() => 'DynamicAccess';
|
| }
|
|
|
| /**
|
| @@ -93,19 +73,7 @@
|
| assert(element != null);
|
| }
|
|
|
| - DartType computeType(Compiler compiler) {
|
| - if (element.isGetter()) {
|
| - FunctionType functionType = element.computeType(compiler);
|
| - return functionType.returnType;
|
| - } else if (element.isSetter()) {
|
| - FunctionType functionType = element.computeType(compiler);
|
| - return functionType.parameterTypes.head;
|
| - } else {
|
| - return element.computeType(compiler);
|
| - }
|
| - }
|
| -
|
| - String toString() => 'ResolvedAccess($element)';
|
| + DartType computeType(Compiler compiler) => element.computeType(compiler);
|
| }
|
|
|
| /**
|
| @@ -121,25 +89,8 @@
|
| Element get element => type.element;
|
|
|
| DartType computeType(Compiler compiler) => type;
|
| -
|
| - String toString() => 'TypeAccess($type)';
|
| }
|
|
|
| -/**
|
| - * An access of a type literal.
|
| - */
|
| -class TypeLiteralAccess extends ElementAccess {
|
| - final Element element;
|
| - TypeLiteralAccess(Element this.element) {
|
| - assert(element != null);
|
| - }
|
| -
|
| - DartType computeType(Compiler compiler) =>
|
| - compiler.typeClass.computeType(compiler);
|
| -
|
| - String toString() => 'TypeLiteralAccess($element)';
|
| -}
|
| -
|
| class TypeCheckerVisitor implements Visitor<DartType> {
|
| final Compiler compiler;
|
| final TreeElements elements;
|
| @@ -147,7 +98,7 @@
|
|
|
| Node lastSeenNode;
|
| DartType expectedReturnType;
|
| - final ClassElement currentClass;
|
| + ClassElement currentClass;
|
|
|
| Link<DartType> cascadeTypes = const Link<DartType>();
|
|
|
| @@ -158,10 +109,7 @@
|
| DartType objectType;
|
| DartType listType;
|
|
|
| - TypeCheckerVisitor(this.compiler, TreeElements elements, this.types)
|
| - : this.elements = elements,
|
| - currentClass = elements.currentElement != null
|
| - ? elements.currentElement.getEnclosingClass() : null {
|
| + TypeCheckerVisitor(this.compiler, this.elements, this.types) {
|
| intType = compiler.intClass.computeType(compiler);
|
| doubleType = compiler.doubleClass.computeType(compiler);
|
| boolType = compiler.boolClass.computeType(compiler);
|
| @@ -174,11 +122,6 @@
|
| compiler.reportWarning(node, new TypeWarning(kind, arguments));
|
| }
|
|
|
| - reportTypeInfo(Spannable node, MessageKind kind, [Map arguments = const {}]) {
|
| - compiler.reportDiagnostic(compiler.spanFromSpannable(node),
|
| - 'Info: ${kind.message(arguments)}', api.Diagnostic.INFO);
|
| - }
|
| -
|
| // TODO(karlklose): remove these functions.
|
| DartType unhandledStatement() => StatementType.NOT_RETURNING;
|
| DartType unhandledExpression() => types.dynamicType;
|
| @@ -217,17 +160,15 @@
|
| * Check if a value of type t can be assigned to a variable,
|
| * parameter or return value of type s.
|
| */
|
| - bool checkAssignable(Node node, DartType from, DartType to) {
|
| - if (!types.isAssignable(from, to)) {
|
| + checkAssignable(Node node, DartType s, DartType t) {
|
| + if (!types.isAssignable(s, t)) {
|
| reportTypeWarning(node, MessageKind.NOT_ASSIGNABLE,
|
| - {'fromType': from, 'toType': to});
|
| - return false;
|
| + {'fromType': s, 'toType': t});
|
| }
|
| - return true;
|
| }
|
|
|
| checkCondition(Expression condition) {
|
| - checkAssignable(condition, analyze(condition), boolType);
|
| + checkAssignable(condition, boolType, analyze(condition));
|
| }
|
|
|
| void pushCascadeType(DartType type) {
|
| @@ -319,6 +260,7 @@
|
| }
|
| DartType previous = expectedReturnType;
|
| expectedReturnType = returnType;
|
| + if (element.isMember()) currentClass = element.getEnclosingClass();
|
| StatementType bodyType = analyze(node.body);
|
| if (returnType != types.voidType && returnType != types.dynamicType
|
| && bodyType != StatementType.RETURNING) {
|
| @@ -355,8 +297,7 @@
|
| return unhandledStatement();
|
| }
|
|
|
| - ElementAccess lookupMember(Node node, DartType type, SourceString name,
|
| - MemberKind memberKind) {
|
| + ElementAccess lookupMethod(Node node, DartType type, SourceString name) {
|
| if (identical(type, types.dynamicType)) {
|
| return const DynamicAccess();
|
| }
|
| @@ -364,35 +305,17 @@
|
| if (member != null) {
|
| return new MemberAccess(member);
|
| }
|
| - switch (memberKind) {
|
| - case MemberKind.METHOD:
|
| - reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND,
|
| - {'className': type.name, 'memberName': name});
|
| - break;
|
| - case MemberKind.OPERATOR:
|
| - reportTypeWarning(node, MessageKind.OPERATOR_NOT_FOUND,
|
| - {'className': type.name, 'memberName': name});
|
| - break;
|
| - case MemberKind.PROPERTY:
|
| - reportTypeWarning(node, MessageKind.PROPERTY_NOT_FOUND,
|
| - {'className': type.name, 'memberName': name});
|
| - break;
|
| - }
|
| + reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND,
|
| + {'className': type.name, 'memberName': name});
|
| return const DynamicAccess();
|
| }
|
|
|
| - DartType lookupMemberType(Node node, DartType type, SourceString name,
|
| - MemberKind memberKind) {
|
| - return lookupMember(node, type, name, memberKind).computeType(compiler);
|
| - }
|
| -
|
| - void analyzeArguments(Send send, Element element, DartType type,
|
| - [LinkBuilder<DartType> argumentTypes]) {
|
| + // TODO(johnniwinther): Provide the element from which the type came in order
|
| + // to give better error messages.
|
| + void analyzeArguments(Send send, DartType type) {
|
| Link<Node> arguments = send.arguments;
|
| DartType unaliasedType = type.unalias(compiler);
|
| if (identical(unaliasedType.kind, TypeKind.FUNCTION)) {
|
| - assert(invariant(send, element != null, message: 'No element for $send'));
|
| - bool error = false;
|
| FunctionType funType = unaliasedType;
|
| Link<DartType> parameterTypes = funType.parameterTypes;
|
| Link<DartType> optionalParameterTypes = funType.optionalParameterTypes;
|
| @@ -405,80 +328,57 @@
|
| DartType namedParameterType =
|
| funType.getNamedParameterType(argumentName);
|
| if (namedParameterType == null) {
|
| - error = true;
|
| // TODO(johnniwinther): Provide better information on the called
|
| // function.
|
| reportTypeWarning(argument, MessageKind.NAMED_ARGUMENT_NOT_FOUND,
|
| {'argumentName': argumentName});
|
|
|
| - DartType argumentType = analyze(argument);
|
| - if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| + analyze(argument);
|
| } else {
|
| - DartType argumentType = analyze(argument);
|
| - if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| - if (!checkAssignable(argument, argumentType, namedParameterType)) {
|
| - error = true;
|
| - }
|
| + checkAssignable(argument, namedParameterType, analyze(argument));
|
| }
|
| } else {
|
| if (parameterTypes.isEmpty) {
|
| if (optionalParameterTypes.isEmpty) {
|
| - error = true;
|
| // TODO(johnniwinther): Provide better information on the
|
| // called function.
|
| reportTypeWarning(argument, MessageKind.ADDITIONAL_ARGUMENT);
|
|
|
| - DartType argumentType = analyze(argument);
|
| - if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| + analyze(argument);
|
| } else {
|
| - DartType argumentType = analyze(argument);
|
| - if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| - if (!checkAssignable(argument,
|
| - argumentType, optionalParameterTypes.head)) {
|
| - error = true;
|
| - }
|
| + checkAssignable(argument, optionalParameterTypes.head,
|
| + analyze(argument));
|
| optionalParameterTypes = optionalParameterTypes.tail;
|
| }
|
| } else {
|
| - DartType argumentType = analyze(argument);
|
| - if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| - if (!checkAssignable(argument, argumentType, parameterTypes.head)) {
|
| - error = true;
|
| - }
|
| + checkAssignable(argument, parameterTypes.head, analyze(argument));
|
| parameterTypes = parameterTypes.tail;
|
| }
|
| }
|
| arguments = arguments.tail;
|
| }
|
| if (!parameterTypes.isEmpty) {
|
| - error = true;
|
| // TODO(johnniwinther): Provide better information on the called
|
| // function.
|
| reportTypeWarning(send, MessageKind.MISSING_ARGUMENT,
|
| {'argumentType': parameterTypes.head});
|
| }
|
| - if (error) {
|
| - reportTypeInfo(element, MessageKind.THIS_IS_THE_METHOD);
|
| - }
|
| } else {
|
| while(!arguments.isEmpty) {
|
| - DartType argumentType = analyze(arguments.head);
|
| - if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| + analyze(arguments.head);
|
| arguments = arguments.tail;
|
| }
|
| }
|
| }
|
|
|
| - DartType analyzeInvocation(Send node, ElementAccess elementAccess,
|
| - [LinkBuilder<DartType> argumentTypes]) {
|
| + DartType analyzeInvocation(Send node, ElementAccess elementAccess) {
|
| DartType type = elementAccess.computeType(compiler);
|
| if (elementAccess.isCallable(compiler)) {
|
| - analyzeArguments(node, elementAccess.element, type, argumentTypes);
|
| + analyzeArguments(node, type);
|
| } else {
|
| reportTypeWarning(node, MessageKind.NOT_CALLABLE,
|
| {'elementName': elementAccess.element.name});
|
| - analyzeArguments(node, elementAccess.element, types.dynamicType,
|
| - argumentTypes);
|
| + analyzeArguments(node, types.dynamicType);
|
| }
|
| if (identical(type.kind, TypeKind.FUNCTION)) {
|
| FunctionType funType = type;
|
| @@ -488,104 +388,6 @@
|
| }
|
| }
|
|
|
| - /**
|
| - * Computes the [ElementAccess] for [name] on the [node] possibly using the
|
| - * [element] provided for [node] by the resolver.
|
| - */
|
| - ElementAccess computeAccess(Send node, SourceString name, Element element,
|
| - MemberKind memberKind) {
|
| - if (node.receiver != null) {
|
| - Element receiverElement = elements[node.receiver];
|
| - if (receiverElement != null) {
|
| - if (receiverElement.isPrefix()) {
|
| - assert(invariant(node, element != null,
|
| - message: 'Prefixed node has no element.'));
|
| - return computeResolvedAccess(node, name, element, memberKind);
|
| - }
|
| - }
|
| - // e.foo() for some expression e.
|
| - DartType receiverType = analyze(node.receiver);
|
| - if (receiverType.isDynamic ||
|
| - receiverType.isMalformed ||
|
| - receiverType.isVoid) {
|
| - return const DynamicAccess();
|
| - }
|
| - TypeKind receiverKind = receiverType.kind;
|
| - if (identical(receiverKind, TypeKind.TYPEDEF)) {
|
| - // TODO(johnniwinther): handle typedefs.
|
| - return const DynamicAccess();
|
| - }
|
| - if (identical(receiverKind, TypeKind.FUNCTION)) {
|
| - // TODO(johnniwinther): handle functions.
|
| - return const DynamicAccess();
|
| - }
|
| - if (identical(receiverKind, TypeKind.TYPE_VARIABLE)) {
|
| - // TODO(johnniwinther): handle type variables.
|
| - return const DynamicAccess();
|
| - }
|
| - assert(invariant(node.receiver,
|
| - identical(receiverKind, TypeKind.INTERFACE),
|
| - message: "interface type expected, got ${receiverKind}"));
|
| - return lookupMember(node, receiverType, name, memberKind);
|
| - } else {
|
| - return computeResolvedAccess(node, name, element, memberKind);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Computes the [ElementAccess] for [name] on the [node] using the [element]
|
| - * provided for [node] by the resolver.
|
| - */
|
| - ElementAccess computeResolvedAccess(Send node, SourceString name,
|
| - Element element, MemberKind memberKind) {
|
| - if (Elements.isUnresolved(element)) {
|
| - // foo() where foo is unresolved.
|
| - return const DynamicAccess();
|
| - } else if (element.isMember()) {
|
| - // foo() where foo is an instance member.
|
| - return lookupMember(node, currentClass.computeType(compiler),
|
| - name, memberKind);
|
| - } else if (element.isFunction()) {
|
| - // foo() where foo is a method in the same class.
|
| - return new ResolvedAccess(element);
|
| - } else if (element.isVariable() ||
|
| - element.isParameter() ||
|
| - element.isField()) {
|
| - // foo() where foo is a field in the same class.
|
| - return new ResolvedAccess(element);
|
| - } else if (element.impliesType()) {
|
| - // The literal `Foo` where Foo is a class, a typedef, or a type variable.
|
| - if (elements.getType(node) != null) {
|
| - assert(invariant(node, identical(compiler.typeClass,
|
| - elements.getType(node).element),
|
| - message: 'Expected type literal type: '
|
| - '${elements.getType(node)}'));
|
| - return new TypeLiteralAccess(element);
|
| - }
|
| - return new ResolvedAccess(element);
|
| - } else if (element.isGetter() || element.isSetter()) {
|
| - return new ResolvedAccess(element);
|
| - } else {
|
| - compiler.internalErrorOnElement(
|
| - element, 'unexpected element kind ${element.kind}');
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Computes the type of the access of [name] on the [node] possibly using the
|
| - * [element] provided for [node] by the resolver.
|
| - */
|
| - DartType computeAccessType(Send node, SourceString name, Element element,
|
| - MemberKind memberKind) {
|
| - DartType type =
|
| - computeAccess(node, name, element, memberKind).computeType(compiler);
|
| - if (type == null) {
|
| - compiler.internalError('type is null on access of $name on $node',
|
| - node: node);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| DartType visitSend(Send node) {
|
| Element element = elements[node];
|
|
|
| @@ -606,252 +408,117 @@
|
| if (node.isOperator && identical(name, 'is')) {
|
| analyze(node.receiver);
|
| return boolType;
|
| - } if (node.isOperator && identical(name, 'as')) {
|
| - analyze(node.receiver);
|
| - return elements.getType(node.arguments.head);
|
| } else if (node.isOperator) {
|
| - final Node receiver = node.receiver;
|
| - final DartType receiverType = analyze(receiver);
|
| - if (identical(name, '==') || identical(name, '!=')
|
| - // TODO(johnniwinther): Remove these.
|
| - || identical(name, '===') || identical(name, '!==')) {
|
| - // Analyze argument.
|
| - analyze(node.arguments.head);
|
| + final Node firstArgument = node.receiver;
|
| + final DartType firstArgumentType = analyze(node.receiver);
|
| + final arguments = node.arguments;
|
| + final Node secondArgument = arguments.isEmpty ? null : arguments.head;
|
| + final DartType secondArgumentType =
|
| + analyzeWithDefault(secondArgument, null);
|
| +
|
| + if (identical(name, '+') || identical(name, '=') ||
|
| + identical(name, '-') || identical(name, '*') ||
|
| + identical(name, '/') || identical(name, '%') ||
|
| + identical(name, '~/') || identical(name, '|') ||
|
| + identical(name, '&') || identical(name, '^') ||
|
| + identical(name, '~')|| identical(name, '<<') ||
|
| + identical(name, '>>') || identical(name, '[]')) {
|
| + return types.dynamicType;
|
| + } else if (identical(name, '<') || identical(name, '>') ||
|
| + identical(name, '<=') || identical(name, '>=') ||
|
| + identical(name, '==') || identical(name, '!=') ||
|
| + identical(name, '===') || identical(name, '!==')) {
|
| return boolType;
|
| } else if (identical(name, '||') ||
|
| - identical(name, '&&')) {
|
| - checkAssignable(receiver, receiverType, boolType);
|
| - final Node argument = node.arguments.head;
|
| - final DartType argumentType = analyze(argument);
|
| - checkAssignable(argument, argumentType, boolType);
|
| + identical(name, '&&') ||
|
| + identical(name, '!')) {
|
| + checkAssignable(firstArgument, boolType, firstArgumentType);
|
| + if (!arguments.isEmpty) {
|
| + // TODO(karlklose): check number of arguments in validator.
|
| + checkAssignable(secondArgument, boolType, secondArgumentType);
|
| + }
|
| return boolType;
|
| - } else if (identical(name, '!')) {
|
| - checkAssignable(receiver, receiverType, boolType);
|
| - return boolType;
|
| - } else if (identical(name, '?')) {
|
| - return boolType;
|
| + } else {
|
| + return unhandledExpression();
|
| }
|
| - SourceString operatorName = selector.source;
|
| - if (identical(name, '-') && node.arguments.isEmpty) {
|
| - operatorName = const SourceString('unary-');
|
| - }
|
| - assert(invariant(node,
|
| - identical(name, '+') || identical(name, '=') ||
|
| - identical(name, '-') || identical(name, '*') ||
|
| - identical(name, '/') || identical(name, '%') ||
|
| - identical(name, '~/') || identical(name, '|') ||
|
| - identical(name, '&') || identical(name, '^') ||
|
| - identical(name, '~')|| identical(name, '<<') ||
|
| - identical(name, '>>') ||
|
| - identical(name, '<') || identical(name, '>') ||
|
| - identical(name, '<=') || identical(name, '>=') ||
|
| - identical(name, '[]'),
|
| - message: 'Unexpected operator $name'));
|
|
|
| - ElementAccess access = lookupMember(node, receiverType,
|
| - operatorName, MemberKind.OPERATOR);
|
| - LinkBuilder<DartType> argumentTypesBuilder = new LinkBuilder<DartType>();
|
| - DartType resultType =
|
| - analyzeInvocation(node, access, argumentTypesBuilder);
|
| - if (identical(receiverType.element, compiler.intClass)) {
|
| - if (identical(name, '+') ||
|
| - identical(operatorName, const SourceString('-')) ||
|
| - identical(name, '*') ||
|
| - identical(name, '%')) {
|
| - DartType argumentType = argumentTypesBuilder.toLink().head;
|
| - if (identical(argumentType.element, compiler.intClass)) {
|
| - return intType;
|
| - } else if (identical(argumentType.element, compiler.doubleClass)) {
|
| - return doubleType;
|
| - }
|
| - }
|
| - }
|
| - return resultType;
|
| } else if (node.isPropertyAccess) {
|
| - ElementAccess access =
|
| - computeAccess(node, selector.source, element, MemberKind.PROPERTY);
|
| - return access.computeType(compiler);
|
| + if (node.receiver != null) {
|
| + // TODO(karlklose): we cannot handle fields.
|
| + return unhandledExpression();
|
| + }
|
| + if (element == null) return types.dynamicType;
|
| + return computeType(element);
|
| +
|
| } else if (node.isFunctionObjectInvocation) {
|
| return unhandledExpression();
|
| } else {
|
| - ElementAccess access =
|
| - computeAccess(node, selector.source, element, MemberKind.METHOD);
|
| - return analyzeInvocation(node, access);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Checks [: target o= value :] for some operator o, and returns the type
|
| - * of the result. This method also handles increment/decrement expressions
|
| - * like [: target++ :].
|
| - */
|
| - DartType checkAssignmentOperator(SendSet node,
|
| - SourceString operatorName,
|
| - Node valueNode,
|
| - DartType value) {
|
| - assert(invariant(node, !node.isIndex));
|
| - Element element = elements[node];
|
| - Identifier selector = node.selector;
|
| - DartType target =
|
| - computeAccessType(node, selector.source, element, MemberKind.PROPERTY);
|
| - // [operator] is the type of operator+ or operator- on [target].
|
| - DartType operator =
|
| - lookupMemberType(node, target, operatorName, MemberKind.OPERATOR);
|
| - if (operator is FunctionType) {
|
| - FunctionType operatorType = operator;
|
| - // [result] is the type of target o value.
|
| - DartType result = operatorType.returnType;
|
| - DartType operatorArgument = operatorType.parameterTypes.head;
|
| - // Check target o value.
|
| - bool validValue = checkAssignable(valueNode, value, operatorArgument);
|
| - if (validValue || !(node.isPrefix || node.isPostfix)) {
|
| - // Check target = result.
|
| - checkAssignable(node.assignmentOperator, result, target);
|
| - }
|
| - return node.isPostfix ? target : result;
|
| - }
|
| - return types.dynamicType;
|
| - }
|
| -
|
| - /**
|
| - * Checks [: base[key] o= value :] for some operator o, and returns the type
|
| - * of the result. This method also handles increment/decrement expressions
|
| - * like [: base[key]++ :].
|
| - */
|
| - DartType checkIndexAssignmentOperator(SendSet node,
|
| - SourceString operatorName,
|
| - Node valueNode,
|
| - DartType value) {
|
| - assert(invariant(node, node.isIndex));
|
| - final DartType base = analyze(node.receiver);
|
| - final Node keyNode = node.arguments.head;
|
| - final DartType key = analyze(keyNode);
|
| -
|
| - // [indexGet] is the type of operator[] on [base].
|
| - DartType indexGet = lookupMemberType(
|
| - node, base, const SourceString('[]'), MemberKind.OPERATOR);
|
| - if (indexGet is FunctionType) {
|
| - FunctionType indexGetType = indexGet;
|
| - DartType indexGetKey = indexGetType.parameterTypes.head;
|
| - // Check base[key].
|
| - bool validKey = checkAssignable(keyNode, key, indexGetKey);
|
| -
|
| - // [element] is the type of base[key].
|
| - DartType element = indexGetType.returnType;
|
| - // [operator] is the type of operator o on [element].
|
| - DartType operator = lookupMemberType(
|
| - node, element, operatorName, MemberKind.OPERATOR);
|
| - if (operator is FunctionType) {
|
| - FunctionType operatorType = operator;
|
| -
|
| - // Check base[key] o value.
|
| - DartType operatorArgument = operatorType.parameterTypes.head;
|
| - bool validValue = checkAssignable(valueNode, value, operatorArgument);
|
| -
|
| - // [result] is the type of base[key] o value.
|
| - DartType result = operatorType.returnType;
|
| -
|
| - // [indexSet] is the type of operator[]= on [base].
|
| - DartType indexSet = lookupMemberType(
|
| - node, base, const SourceString('[]='), MemberKind.OPERATOR);
|
| - if (indexSet is FunctionType) {
|
| - FunctionType indexSetType = indexSet;
|
| - DartType indexSetKey = indexSetType.parameterTypes.head;
|
| - DartType indexSetValue =
|
| - indexSetType.parameterTypes.tail.head;
|
| -
|
| - if (validKey || indexGetKey != indexSetKey) {
|
| - // Only check base[key] on []= if base[key] was valid for [] or
|
| - // if the key types differ.
|
| - checkAssignable(keyNode, key, indexSetKey);
|
| + ElementAccess computeMethod() {
|
| + if (node.receiver != null) {
|
| + // e.foo() for some expression e.
|
| + DartType receiverType = analyze(node.receiver);
|
| + if (receiverType.element == compiler.dynamicClass ||
|
| + receiverType == null ||
|
| + receiverType.isMalformed ||
|
| + receiverType.isVoid) {
|
| + return const DynamicAccess();
|
| }
|
| - // Check base[key] = result
|
| - if (validValue || !(node.isPrefix || node.isPostfix)) {
|
| - checkAssignable(node.assignmentOperator, result, indexSetValue);
|
| + TypeKind receiverKind = receiverType.kind;
|
| + if (identical(receiverKind, TypeKind.TYPEDEF)) {
|
| + // TODO(karlklose): handle typedefs.
|
| + return const DynamicAccess();
|
| }
|
| + if (identical(receiverKind, TypeKind.TYPE_VARIABLE)) {
|
| + // TODO(karlklose): handle type variables.
|
| + return const DynamicAccess();
|
| + }
|
| + if (identical(receiverKind, TypeKind.FUNCTION)) {
|
| + // TODO(karlklose): handle getters.
|
| + return const DynamicAccess();
|
| + }
|
| + assert(invariant(node.receiver,
|
| + identical(receiverKind, TypeKind.INTERFACE),
|
| + message: "interface type expected, got ${receiverKind}"));
|
| + return lookupMethod(selector, receiverType, selector.source);
|
| + } else {
|
| + if (Elements.isUnresolved(element)) {
|
| + // foo() where foo is unresolved.
|
| + return const DynamicAccess();
|
| + } else if (element.isFunction()) {
|
| + // foo() where foo is a method in the same class.
|
| + return new ResolvedAccess(element);
|
| + } else if (element.isVariable() || element.isField()) {
|
| + // foo() where foo is a field in the same class.
|
| + return new ResolvedAccess(element);
|
| + } else if (element.isGetter()) {
|
| + // TODO(karlklose): handle getters.
|
| + return const DynamicAccess();
|
| + } else if (element.isClass()) {
|
| + // TODO(karlklose): handle type literals.
|
| + return const DynamicAccess();
|
| + } else {
|
| + compiler.internalErrorOnElement(
|
| + element, 'unexpected element kind ${element.kind}');
|
| + }
|
| }
|
| - return node.isPostfix ? element : result;
|
| }
|
| + return analyzeInvocation(node, computeMethod());
|
| }
|
| - return types.dynamicType;
|
| }
|
|
|
| visitSendSet(SendSet node) {
|
| - Element element = elements[node];
|
| Identifier selector = node.selector;
|
| final name = node.assignmentOperator.source.stringValue;
|
| - if (identical(name, '=')) {
|
| - // e1 = value
|
| - if (node.isIndex) {
|
| - // base[key] = value
|
| - final DartType base = analyze(node.receiver);
|
| - final Node keyNode = node.arguments.head;
|
| - final DartType key = analyze(keyNode);
|
| - final Node valueNode = node.arguments.tail.head;
|
| - final DartType value = analyze(valueNode);
|
| - DartType indexSet = lookupMemberType(
|
| - node, base, const SourceString('[]='), MemberKind.OPERATOR);
|
| - if (indexSet is FunctionType) {
|
| - FunctionType indexSetType = indexSet;
|
| - DartType indexSetKey = indexSetType.parameterTypes.head;
|
| - checkAssignable(keyNode, key, indexSetKey);
|
| - DartType indexSetValue = indexSetType.parameterTypes.tail.head;
|
| - checkAssignable(node.assignmentOperator, value, indexSetValue);
|
| - }
|
| - return value;
|
| - } else {
|
| - // target = value
|
| - DartType target = computeAccessType(node, selector.source,
|
| - element, MemberKind.PROPERTY);
|
| - final Node valueNode = node.arguments.head;
|
| - final DartType value = analyze(valueNode);
|
| - checkAssignable(node.assignmentOperator, value, target);
|
| - return value;
|
| - }
|
| - } else if (identical(name, '++') || identical(name, '--')) {
|
| - // e++ or e--
|
| - SourceString operatorName = identical(name, '++')
|
| - ? const SourceString('+') : const SourceString('-');
|
| - if (node.isIndex) {
|
| - // base[key]++, base[key]--, ++base[key], or --base[key]
|
| - return checkIndexAssignmentOperator(
|
| - node, operatorName, node.assignmentOperator, intType);
|
| - } else {
|
| - // target++, target--, ++target, or --target
|
| - return checkAssignmentOperator(
|
| - node, operatorName, node.assignmentOperator, intType);
|
| - }
|
| + if (identical(name, '++') || identical(name, '--')) {
|
| + final Element element = elements[node.selector];
|
| + final DartType receiverType = computeType(element);
|
| + // TODO(karlklose): this should be the return type instead of int.
|
| + return node.isPrefix ? intType : receiverType;
|
| } else {
|
| - // e1 o= e2 for some operator o.
|
| - SourceString operatorName;
|
| - switch (name) {
|
| - case '+=': operatorName = const SourceString('+'); break;
|
| - case '-=': operatorName = const SourceString('-'); break;
|
| - case '*=': operatorName = const SourceString('*'); break;
|
| - case '/=': operatorName = const SourceString('/'); break;
|
| - case '%=': operatorName = const SourceString('%'); break;
|
| - case '~/=': operatorName = const SourceString('~/'); break;
|
| - case '&=': operatorName = const SourceString('&'); break;
|
| - case '|=': operatorName = const SourceString('|'); break;
|
| - case '^=': operatorName = const SourceString('^'); break;
|
| - case '<<=': operatorName = const SourceString('<<'); break;
|
| - case '>>=': operatorName = const SourceString('>>'); break;
|
| - default:
|
| - compiler.internalError(
|
| - 'Unexpected assignment operator $name', node: node);
|
| - }
|
| - if (node.isIndex) {
|
| - // base[key] o= value for some operator o.
|
| - final Node valueNode = node.arguments.tail.head;
|
| - final DartType value = analyze(valueNode);
|
| - return checkIndexAssignmentOperator(
|
| - node, operatorName, valueNode, value);
|
| - } else {
|
| - // target o= value for some operator o.
|
| - final Node valueNode = node.arguments.head;
|
| - final DartType value = analyze(valueNode);
|
| - return checkAssignmentOperator(node, operatorName, valueNode, value);
|
| - }
|
| + DartType targetType = computeType(elements[node]);
|
| + Node value = node.arguments.head;
|
| + checkAssignable(value, targetType, analyze(value));
|
| + return targetType;
|
| }
|
| }
|
|
|
| @@ -883,30 +550,11 @@
|
|
|
| DartType visitNewExpression(NewExpression node) {
|
| Element element = elements[node.send];
|
| - DartType constructorType = computeType(element);
|
| - DartType newType = elements.getType(node);
|
| - // TODO(johnniwinther): Use [:lookupMember:] to account for type variable
|
| - // substitution of parameter types.
|
| - if (identical(newType.kind, TypeKind.INTERFACE)) {
|
| - InterfaceType newInterfaceType = newType;
|
| - constructorType = constructorType.subst(
|
| - newInterfaceType.typeArguments,
|
| - newInterfaceType.element.typeVariables);
|
| - }
|
| - analyzeArguments(node.send, element, constructorType);
|
| - return newType;
|
| + analyzeArguments(node.send, computeType(element));
|
| + return analyze(node.send.selector);
|
| }
|
|
|
| DartType visitLiteralList(LiteralList node) {
|
| - InterfaceType listType = elements.getType(node);
|
| - DartType listElementType = listType.typeArguments.head;
|
| - for (Link<Node> link = node.elements.nodes;
|
| - !link.isEmpty;
|
| - link = link.tail) {
|
| - Node element = link.head;
|
| - DartType elementType = analyze(element);
|
| - checkAssignable(element, elementType, listElementType);
|
| - }
|
| return listType;
|
| }
|
|
|
| @@ -962,7 +610,7 @@
|
| && !types.isAssignable(expressionType, types.voidType)) {
|
| reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID);
|
| } else {
|
| - checkAssignable(expression, expressionType, expectedReturnType);
|
| + checkAssignable(expression, expectedReturnType, expressionType);
|
| }
|
|
|
| // Let f be the function immediately enclosing a return statement of the
|
| @@ -982,7 +630,6 @@
|
| return types.dynamicType;
|
| }
|
|
|
| - // TODO(johnniwinther): Remove this.
|
| DartType computeType(Element element) {
|
| if (Elements.isUnresolved(element)) return types.dynamicType;
|
| DartType result = element.computeType(compiler);
|
| @@ -1005,12 +652,12 @@
|
| }
|
| for (Link<Node> link = node.definitions.nodes; !link.isEmpty;
|
| link = link.tail) {
|
| - Node definition = link.head;
|
| - compiler.ensure(definition is Identifier || definition is SendSet);
|
| - if (definition is Send) {
|
| - SendSet initialization = definition;
|
| - DartType initializer = analyzeNonVoid(initialization.arguments.head);
|
| - checkAssignable(initialization.assignmentOperator, initializer, type);
|
| + Node initialization = link.head;
|
| + compiler.ensure(initialization is Identifier
|
| + || initialization is Send);
|
| + if (initialization is Send) {
|
| + DartType initializer = analyzeNonVoid(link.head);
|
| + checkAssignable(node, type, initializer);
|
| }
|
| }
|
| return StatementType.NOT_RETURNING;
|
| @@ -1086,19 +733,7 @@
|
| }
|
|
|
| visitLiteralMap(LiteralMap node) {
|
| - InterfaceType mapType = elements.getType(node);
|
| - DartType mapKeyType = mapType.typeArguments.head;
|
| - DartType mapValueType = mapType.typeArguments.tail.head;
|
| - for (Link<Node> link = node.entries.nodes;
|
| - !link.isEmpty;
|
| - link = link.tail) {
|
| - LiteralMapEntry entry = link.head;
|
| - DartType keyType = analyze(entry.key);
|
| - checkAssignable(entry.key, keyType, mapKeyType);
|
| - DartType valueType = analyze(entry.value);
|
| - checkAssignable(entry.value, valueType, mapValueType);
|
| - }
|
| - return mapType;
|
| + return unhandledExpression();
|
| }
|
|
|
| visitLiteralMapEntry(LiteralMapEntry node) {
|
|
|