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

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

Issue 13084013: Handle assignability for Send and SendSet (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix dart2dart bugs. Created 7 years, 7 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
Index: sdk/lib/_internal/compiler/implementation/typechecker.dart
diff --git a/sdk/lib/_internal/compiler/implementation/typechecker.dart b/sdk/lib/_internal/compiler/implementation/typechecker.dart
index 24faf7776fb1274276f8916f7756920112574080..b58a0eb6c5e99eb269c61455a61225bb5b4b5215 100644
--- a/sdk/lib/_internal/compiler/implementation/typechecker.dart
+++ b/sdk/lib/_internal/compiler/implementation/typechecker.dart
@@ -18,6 +18,22 @@ class TypeCheckerTask extends CompilerTask {
}
/**
+ * 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.
*/
@@ -49,6 +65,8 @@ class MemberAccess extends ElementAccess {
Element get element => member.element;
DartType computeType(Compiler compiler) => member.computeType(compiler);
+
+ String toString() => 'MemberAccess($member)';
}
/// An access of an unresolved element.
@@ -60,6 +78,8 @@ class DynamicAccess implements ElementAccess {
DartType computeType(Compiler compiler) => compiler.types.dynamicType;
bool isCallable(Compiler compiler) => true;
+
+ String toString() => 'DynamicAccess';
}
/**
@@ -73,7 +93,19 @@ class ResolvedAccess extends ElementAccess {
assert(element != null);
}
- DartType computeType(Compiler compiler) => element.computeType(compiler);
+ 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)';
}
/**
@@ -89,6 +121,23 @@ class TypeAccess extends ElementAccess {
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> {
@@ -98,7 +147,7 @@ class TypeCheckerVisitor implements Visitor<DartType> {
Node lastSeenNode;
DartType expectedReturnType;
- ClassElement currentClass;
+ final ClassElement currentClass;
Link<DartType> cascadeTypes = const Link<DartType>();
@@ -109,7 +158,10 @@ class TypeCheckerVisitor implements Visitor<DartType> {
DartType objectType;
DartType listType;
- TypeCheckerVisitor(this.compiler, this.elements, this.types) {
+ TypeCheckerVisitor(this.compiler, TreeElements elements, this.types)
+ : this.elements = elements,
+ currentClass = elements.currentElement != null
+ ? elements.currentElement.getEnclosingClass() : null {
intType = compiler.intClass.computeType(compiler);
doubleType = compiler.doubleClass.computeType(compiler);
boolType = compiler.boolClass.computeType(compiler);
@@ -122,6 +174,11 @@ class TypeCheckerVisitor implements Visitor<DartType> {
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;
@@ -160,15 +217,17 @@ class TypeCheckerVisitor implements Visitor<DartType> {
* Check if a value of type t can be assigned to a variable,
* parameter or return value of type s.
*/
- checkAssignable(Node node, DartType s, DartType t) {
- if (!types.isAssignable(s, t)) {
+ bool checkAssignable(Node node, DartType from, DartType to) {
+ if (!types.isAssignable(from, to)) {
reportTypeWarning(node, MessageKind.NOT_ASSIGNABLE,
- {'fromType': s, 'toType': t});
+ {'fromType': from, 'toType': to});
+ return false;
}
+ return true;
}
checkCondition(Expression condition) {
- checkAssignable(condition, boolType, analyze(condition));
+ checkAssignable(condition, analyze(condition), boolType);
}
void pushCascadeType(DartType type) {
@@ -260,7 +319,6 @@ class TypeCheckerVisitor implements Visitor<DartType> {
}
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) {
@@ -297,7 +355,8 @@ class TypeCheckerVisitor implements Visitor<DartType> {
return unhandledStatement();
}
- ElementAccess lookupMethod(Node node, DartType type, SourceString name) {
+ ElementAccess lookupMember(Node node, DartType type, SourceString name,
+ MemberKind memberKind) {
if (identical(type, types.dynamicType)) {
return const DynamicAccess();
}
@@ -305,17 +364,35 @@ class TypeCheckerVisitor implements Visitor<DartType> {
if (member != null) {
return new MemberAccess(member);
}
- reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND,
- {'className': type.name, 'memberName': name});
+ 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;
+ }
return const DynamicAccess();
}
- // TODO(johnniwinther): Provide the element from which the type came in order
- // to give better error messages.
- void analyzeArguments(Send send, DartType type) {
+ 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]) {
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;
@@ -328,57 +405,80 @@ class TypeCheckerVisitor implements Visitor<DartType> {
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});
- analyze(argument);
+ DartType argumentType = analyze(argument);
+ if (argumentTypes != null) argumentTypes.addLast(argumentType);
} else {
- checkAssignable(argument, namedParameterType, analyze(argument));
+ DartType argumentType = analyze(argument);
+ if (argumentTypes != null) argumentTypes.addLast(argumentType);
+ if (!checkAssignable(argument, argumentType, namedParameterType)) {
+ error = true;
+ }
}
} else {
if (parameterTypes.isEmpty) {
if (optionalParameterTypes.isEmpty) {
+ error = true;
// TODO(johnniwinther): Provide better information on the
// called function.
reportTypeWarning(argument, MessageKind.ADDITIONAL_ARGUMENT);
- analyze(argument);
+ DartType argumentType = analyze(argument);
+ if (argumentTypes != null) argumentTypes.addLast(argumentType);
} else {
- checkAssignable(argument, optionalParameterTypes.head,
- analyze(argument));
+ DartType argumentType = analyze(argument);
+ if (argumentTypes != null) argumentTypes.addLast(argumentType);
+ if (!checkAssignable(argument,
+ argumentType, optionalParameterTypes.head)) {
+ error = true;
+ }
optionalParameterTypes = optionalParameterTypes.tail;
}
} else {
- checkAssignable(argument, parameterTypes.head, analyze(argument));
+ DartType argumentType = analyze(argument);
+ if (argumentTypes != null) argumentTypes.addLast(argumentType);
+ if (!checkAssignable(argument, argumentType, parameterTypes.head)) {
+ error = true;
+ }
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) {
- analyze(arguments.head);
+ DartType argumentType = analyze(arguments.head);
+ if (argumentTypes != null) argumentTypes.addLast(argumentType);
arguments = arguments.tail;
}
}
}
- DartType analyzeInvocation(Send node, ElementAccess elementAccess) {
+ DartType analyzeInvocation(Send node, ElementAccess elementAccess,
+ [LinkBuilder<DartType> argumentTypes]) {
DartType type = elementAccess.computeType(compiler);
if (elementAccess.isCallable(compiler)) {
- analyzeArguments(node, type);
+ analyzeArguments(node, elementAccess.element, type, argumentTypes);
} else {
reportTypeWarning(node, MessageKind.NOT_CALLABLE,
{'elementName': elementAccess.element.name});
- analyzeArguments(node, types.dynamicType);
+ analyzeArguments(node, elementAccess.element, types.dynamicType,
+ argumentTypes);
}
if (identical(type.kind, TypeKind.FUNCTION)) {
FunctionType funType = type;
@@ -388,6 +488,104 @@ class TypeCheckerVisitor implements Visitor<DartType> {
}
}
+ /**
+ * 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];
@@ -408,117 +606,252 @@ class TypeCheckerVisitor implements Visitor<DartType> {
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 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, '!==')) {
+ 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);
return boolType;
} else if (identical(name, '||') ||
- identical(name, '&&') ||
- identical(name, '!')) {
- checkAssignable(firstArgument, boolType, firstArgumentType);
- if (!arguments.isEmpty) {
- // TODO(karlklose): check number of arguments in validator.
- checkAssignable(secondArgument, boolType, secondArgumentType);
- }
+ identical(name, '&&')) {
+ checkAssignable(receiver, receiverType, boolType);
+ final Node argument = node.arguments.head;
+ final DartType argumentType = analyze(argument);
+ checkAssignable(argument, argumentType, boolType);
+ return boolType;
+ } else if (identical(name, '!')) {
+ checkAssignable(receiver, receiverType, boolType);
+ return boolType;
+ } else if (identical(name, '?')) {
return boolType;
- } else {
- return unhandledExpression();
}
-
- } else if (node.isPropertyAccess) {
- if (node.receiver != null) {
- // TODO(karlklose): we cannot handle fields.
- return unhandledExpression();
+ SourceString operatorName = selector.source;
+ if (identical(name, '-') && node.arguments.isEmpty) {
+ operatorName = const SourceString('unary-');
}
- if (element == null) return types.dynamicType;
- return computeType(element);
-
+ 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);
} else if (node.isFunctionObjectInvocation) {
return unhandledExpression();
} else {
- 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();
- }
- 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();
+ 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);
}
- 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}');
+ // Check base[key] = result
+ if (validValue || !(node.isPrefix || node.isPostfix)) {
+ checkAssignable(node.assignmentOperator, result, indexSetValue);
}
}
+ 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, '++') || 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;
+ 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);
+ }
} else {
- DartType targetType = computeType(elements[node]);
- Node value = node.arguments.head;
- checkAssignable(value, targetType, analyze(value));
- return targetType;
+ // 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);
+ }
}
}
@@ -550,11 +883,30 @@ class TypeCheckerVisitor implements Visitor<DartType> {
DartType visitNewExpression(NewExpression node) {
Element element = elements[node.send];
- analyzeArguments(node.send, computeType(element));
- return analyze(node.send.selector);
+ 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;
}
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;
}
@@ -610,7 +962,7 @@ class TypeCheckerVisitor implements Visitor<DartType> {
&& !types.isAssignable(expressionType, types.voidType)) {
reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID);
} else {
- checkAssignable(expression, expectedReturnType, expressionType);
+ checkAssignable(expression, expressionType, expectedReturnType);
}
// Let f be the function immediately enclosing a return statement of the
@@ -630,6 +982,7 @@ class TypeCheckerVisitor implements Visitor<DartType> {
return types.dynamicType;
}
+ // TODO(johnniwinther): Remove this.
DartType computeType(Element element) {
if (Elements.isUnresolved(element)) return types.dynamicType;
DartType result = element.computeType(compiler);
@@ -652,12 +1005,12 @@ class TypeCheckerVisitor implements Visitor<DartType> {
}
for (Link<Node> link = node.definitions.nodes; !link.isEmpty;
link = link.tail) {
- 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);
+ 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);
}
}
return StatementType.NOT_RETURNING;
@@ -733,7 +1086,19 @@ class TypeCheckerVisitor implements Visitor<DartType> {
}
visitLiteralMap(LiteralMap node) {
- return unhandledExpression();
+ 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;
}
visitLiteralMapEntry(LiteralMapEntry node) {

Powered by Google App Engine
This is Rietveld 408576698