Index: pkg/compiler/lib/src/typechecker.dart |
diff --git a/pkg/compiler/lib/src/typechecker.dart b/pkg/compiler/lib/src/typechecker.dart |
index cbb5c246a66d1ee357fc11a5a80400aed921869d..0e6df8f3a70192ec39c3ac4def7e75df9fdde44f 100644 |
--- a/pkg/compiler/lib/src/typechecker.dart |
+++ b/pkg/compiler/lib/src/typechecker.dart |
@@ -417,10 +417,14 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
// TODO(karlklose): remove these functions. |
ResolutionDartType unhandledExpression() => const ResolutionDynamicType(); |
+ /// Checks that the analyzed node is not `void`. |
+ /// |
+ /// If it is, a warning is emitted, and the returned type is `dynamic`. |
ResolutionDartType analyzeNonVoid(Node node) { |
ResolutionDartType type = analyze(node); |
if (type.isVoid) { |
reportTypeWarning(node, MessageKind.VOID_EXPRESSION); |
+ return const ResolutionDynamicType(); |
} |
return type; |
} |
@@ -539,6 +543,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
} |
/// Analyze [node] in the context of the known types shown in [context]. |
+ /// |
+ /// If the node [mustHaveType] then it must also not be void. |
ResolutionDartType analyzeInPromotedContext(Node context, Node node, |
{bool mustHaveType: true}) { |
Link<TypePromotion> knownForNode = const Link<TypePromotion>(); |
@@ -549,7 +555,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
registerKnownTypePromotion(typePromotion); |
} |
- final ResolutionDartType type = analyze(node, mustHaveType: mustHaveType); |
+ final ResolutionDartType type = mustHaveType |
+ ? analyzeNonVoid(node) |
+ : analyze(node, mustHaveType: false); |
while (!knownForNode.isEmpty) { |
unregisterKnownTypePromotion(knownForNode.head); |
@@ -581,7 +589,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
} |
checkCondition(Expression condition) { |
- checkAssignable(condition, analyze(condition), boolType); |
+ checkAssignable(condition, analyzeNonVoid(condition), boolType); |
} |
void pushCascadeType(ResolutionDartType type) { |
@@ -595,8 +603,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
} |
visitAssert(Assert node) { |
- analyze(node.condition); |
- if (node.hasMessage) analyze(node.message); |
+ analyzeNonVoid(node.condition); |
+ if (node.hasMessage) analyzeNonVoid(node.message); |
} |
visitBlock(Block node) { |
@@ -609,7 +617,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
} |
ResolutionDartType visitCascadeReceiver(CascadeReceiver node) { |
- ResolutionDartType type = analyze(node.expression); |
+ ResolutionDartType type = analyzeNonVoid(node.expression); |
pushCascadeType(type); |
return type; |
} |
@@ -939,10 +947,10 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
MessageKind.NAMED_ARGUMENT_NOT_FOUND, |
{'argumentName': argumentName})); |
- ResolutionDartType argumentType = analyze(argument); |
+ ResolutionDartType argumentType = analyzeNonVoid(argument); |
if (argumentTypes != null) argumentTypes.addLast(argumentType); |
} else { |
- ResolutionDartType argumentType = analyze(argument); |
+ ResolutionDartType argumentType = analyzeNonVoid(argument); |
if (argumentTypes != null) argumentTypes.addLast(argumentType); |
checkAssignable(argument, argumentType, namedParameterType); |
} |
@@ -954,16 +962,16 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
reportWarning(reporter.createMessage( |
argument, MessageKind.ADDITIONAL_ARGUMENT)); |
- ResolutionDartType argumentType = analyze(argument); |
+ ResolutionDartType argumentType = analyzeNonVoid(argument); |
if (argumentTypes != null) argumentTypes.addLast(argumentType); |
} else { |
- ResolutionDartType argumentType = analyze(argument); |
+ ResolutionDartType argumentType = analyzeNonVoid(argument); |
if (argumentTypes != null) argumentTypes.addLast(argumentType); |
checkAssignable( |
argument, argumentType, optionalParameterTypes.current); |
} |
} else { |
- ResolutionDartType argumentType = analyze(argument); |
+ ResolutionDartType argumentType = analyzeNonVoid(argument); |
if (argumentTypes != null) argumentTypes.addLast(argumentType); |
checkAssignable(argument, argumentType, parameterTypes.current); |
} |
@@ -978,7 +986,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
} |
} else { |
while (!arguments.isEmpty) { |
- ResolutionDartType argumentType = analyze(arguments.head); |
+ ResolutionDartType argumentType = analyzeNonVoid(arguments.head); |
if (argumentTypes != null) argumentTypes.addLast(argumentType); |
arguments = arguments.tail; |
} |
@@ -1034,8 +1042,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
} |
} |
// e.foo() for some expression e. |
- ResolutionDartType receiverType = analyze(node.receiver); |
- if (receiverType.treatAsDynamic || receiverType.isVoid) { |
+ ResolutionDartType receiverType = analyzeNonVoid(node.receiver); |
+ if (receiverType.treatAsDynamic) { |
return const DynamicAccess(); |
} |
return lookupMember( |
@@ -1260,7 +1268,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
return elements.getType(node.arguments.head); |
} else if (node.isOperator) { |
final Node receiver = node.receiver; |
- final ResolutionDartType receiverType = analyze(receiver); |
+ final ResolutionDartType receiverType = analyzeNonVoid(receiver); |
if (identical(name, '==') || |
identical(name, '!=') |
// TODO(johnniwinther): Remove these. |
@@ -1268,12 +1276,12 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
identical(name, '===') || |
identical(name, '!==')) { |
// Analyze argument. |
- analyze(node.arguments.head); |
+ analyzeNonVoid(node.arguments.head); |
return boolType; |
} else if (identical(name, '||')) { |
checkAssignable(receiver, receiverType, boolType); |
final Node argument = node.arguments.head; |
- final ResolutionDartType argumentType = analyze(argument); |
+ final ResolutionDartType argumentType = analyzeNonVoid(argument); |
checkAssignable(argument, argumentType, boolType); |
return boolType; |
} else if (identical(name, '&&')) { |
@@ -1294,7 +1302,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
return boolType; |
} else if (identical(name, '??')) { |
final Node argument = node.arguments.head; |
- final ResolutionDartType argumentType = analyze(argument); |
+ final ResolutionDartType argumentType = analyzeNonVoid(argument); |
return types.computeLeastUpperBound(receiverType, argumentType); |
} |
String operatorName = selector.source; |
@@ -1323,12 +1331,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
identical(name, '[]'), |
message: 'Unexpected operator $name')); |
- // TODO(karlklose): handle `void` in expression context by calling |
- // [analyzeNonVoid] instead of [analyze]. |
- ElementAccess access = receiverType.isVoid |
- ? const DynamicAccess() |
- : lookupMember( |
- node, receiverType, operatorName, MemberKind.OPERATOR, null); |
+ ElementAccess access = lookupMember( |
+ node, receiverType, operatorName, MemberKind.OPERATOR, null); |
LinkBuilder<ResolutionDartType> argumentTypesBuilder = |
new LinkBuilder<ResolutionDartType>(); |
ResolutionDartType resultType = |
@@ -1386,6 +1390,10 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
Identifier selector = node.selector; |
ResolutionDartType getter = computeAccessType( |
node, selector.source, getterElement, MemberKind.GETTER); |
+ if (getter.isVoid) { |
+ reportTypeWarning(node, MessageKind.VOID_EXPRESSION); |
+ getter = const ResolutionDynamicType(); |
+ } |
ResolutionDartType setter = computeAccessType( |
node, selector.source, setterElement, MemberKind.SETTER); |
// [operator] is the type of operator+ or operator- on [target]. |
@@ -1416,9 +1424,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
ResolutionDartType checkIndexAssignmentOperator(SendSet node, |
String operatorName, Node valueNode, ResolutionDartType value) { |
assert(invariant(node, node.isIndex)); |
- final ResolutionDartType base = analyze(node.receiver); |
+ final ResolutionDartType base = analyzeNonVoid(node.receiver); |
final Node keyNode = node.arguments.head; |
- final ResolutionDartType key = analyze(keyNode); |
+ final ResolutionDartType key = analyzeNonVoid(keyNode); |
// [indexGet] is the type of operator[] on [base]. |
ResolutionDartType indexGet = |
@@ -1479,11 +1487,11 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
// e1 = value |
if (node.isIndex) { |
// base[key] = value |
- final ResolutionDartType base = analyze(node.receiver); |
+ final ResolutionDartType base = analyzeNonVoid(node.receiver); |
final Node keyNode = node.arguments.head; |
- final ResolutionDartType key = analyze(keyNode); |
+ final ResolutionDartType key = analyzeNonVoid(keyNode); |
final Node valueNode = node.arguments.tail.head; |
- final ResolutionDartType value = analyze(valueNode); |
+ final ResolutionDartType value = analyzeNonVoid(valueNode); |
ResolutionDartType indexSet = |
lookupMemberType(node, base, '[]=', MemberKind.OPERATOR); |
ResolutionDartType indexSetValue = const ResolutionDynamicType(); |
@@ -1514,7 +1522,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
node, selector.source, element, MemberKind.SETTER); |
} |
final Node valueNode = node.arguments.head; |
- final ResolutionDartType value = analyze(valueNode); |
+ final ResolutionDartType value = analyzeNonVoid(valueNode); |
checkAssignable(node.assignmentOperator, value, target); |
return identical(name, '=') |
? value |
@@ -1575,13 +1583,13 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
if (node.isIndex) { |
// base[key] o= value for some operator o. |
final Node valueNode = node.arguments.tail.head; |
- final ResolutionDartType value = analyze(valueNode); |
+ final ResolutionDartType value = analyzeNonVoid(valueNode); |
return checkIndexAssignmentOperator( |
node, operatorName, valueNode, value); |
} else { |
// target o= value for some operator o. |
final Node valueNode = node.arguments.head; |
- final ResolutionDartType value = analyze(valueNode); |
+ final ResolutionDartType value = analyzeNonVoid(valueNode); |
return checkAssignmentOperator(node, operatorName, valueNode, value); |
} |
} |
@@ -1604,8 +1612,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
} |
ResolutionDartType visitStringJuxtaposition(StringJuxtaposition node) { |
- analyze(node.first); |
- analyze(node.second); |
+ analyzeNonVoid(node.first); |
+ analyzeNonVoid(node.second); |
return stringType; |
} |
@@ -1662,7 +1670,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
!link.isEmpty; |
link = link.tail) { |
Node element = link.head; |
- ResolutionDartType elementType = analyze(element); |
+ ResolutionDartType elementType = analyzeNonVoid(element); |
checkAssignable(element, elementType, listElementType, |
isConst: node.isConst); |
} |
@@ -1705,6 +1713,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
commonElements.futureType(types.flatten(expressionType)); |
expressionType = futureOfFlattenedType; |
} |
+ // TODO(floitsch): we want to break this exception. |
+ // No return x; allowed in a void function. Even if the return type is |
+ // void. |
if (expectedReturnType.isVoid && |
!types.isAssignable(expressionType, const ResolutionVoidType())) { |
reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID); |
@@ -1716,6 +1727,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
// `return;` is allowed. |
} else if (!types.isAssignable( |
expectedReturnType, const ResolutionVoidType())) { |
+ // TODO(floitsch): this is probably where we want to have the checks |
+ // that `void` must not have a `return` unless it returns void. |
+ |
// Let f be the function immediately enclosing a return statement of the |
// form 'return;' It is a static warning if both of the following |
// conditions hold: |
@@ -1728,7 +1742,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
ResolutionDartType visitThrow(Throw node) { |
// TODO(johnniwinther): Handle reachability. |
- analyze(node.expression); |
+ analyzeNonVoid(node.expression); |
return const ResolutionDynamicType(); |
} |
@@ -1742,7 +1756,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
} |
visitYield(Yield node) { |
- ResolutionDartType resultType = analyze(node.expression); |
+ ResolutionDartType resultType = analyzeNonVoid(node.expression); |
if (!node.hasStar) { |
if (currentAsyncMarker.isAsync) { |
ResolutionInterfaceType streamOfResultType = |
@@ -1841,7 +1855,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
ResolutionDartType thenType = |
analyzeInPromotedContext(condition, thenExpression); |
- ResolutionDartType elseType = analyze(node.elseExpression); |
+ ResolutionDartType elseType = analyzeNonVoid(node.elseExpression); |
return types.computeLeastUpperBound(thenType, elseType); |
} |
@@ -1880,7 +1894,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
visitAsyncForIn(AsyncForIn node) { |
ResolutionDartType elementType = computeForInElementType(node); |
- ResolutionDartType expressionType = analyze(node.expression); |
+ ResolutionDartType expressionType = analyzeNonVoid(node.expression); |
if (resolution.target.supportsAsyncAwait) { |
ResolutionInterfaceType streamOfDynamic = commonElements.streamType(); |
if (!types.isAssignable(expressionType, streamOfDynamic)) { |
@@ -1896,7 +1910,10 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
if (streamType != null) { |
ResolutionDartType streamElementType = |
streamType.typeArguments.first; |
- if (!types.isAssignable(streamElementType, elementType)) { |
+ if (streamElementType.isVoid) { |
+ reportTypeWarning( |
+ node.expression, MessageKind.AWAIT_FORIN_VOID_GENERIC); |
+ } else if (!types.isAssignable(streamElementType, elementType)) { |
reportMessage( |
node.expression, |
MessageKind.FORIN_NOT_ASSIGNABLE, |
@@ -1916,13 +1933,15 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
visitSyncForIn(SyncForIn node) { |
ResolutionDartType elementType = computeForInElementType(node); |
- ResolutionDartType expressionType = analyze(node.expression); |
+ ResolutionDartType expressionType = analyzeNonVoid(node.expression); |
ResolutionDartType iteratorType = lookupMemberType(node.expression, |
expressionType, Identifiers.iterator, MemberKind.GETTER); |
ResolutionDartType currentType = lookupMemberType( |
node.expression, iteratorType, Identifiers.current, MemberKind.GETTER, |
isHint: true); |
- if (!types.isAssignable(currentType, elementType)) { |
+ if (currentType.isVoid) { |
+ reportTypeWarning(node.expression, MessageKind.FORIN_VOID_GENERIC); |
+ } else if (!types.isAssignable(currentType, elementType)) { |
reportMessage( |
node.expression, |
MessageKind.FORIN_NOT_ASSIGNABLE, |
@@ -1949,9 +1968,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
!link.isEmpty; |
link = link.tail) { |
LiteralMapEntry entry = link.head; |
- ResolutionDartType keyType = analyze(entry.key); |
+ ResolutionDartType keyType = analyzeNonVoid(entry.key); |
checkAssignable(entry.key, keyType, mapKeyType, isConst: isConst); |
- ResolutionDartType valueType = analyze(entry.value); |
+ ResolutionDartType valueType = analyzeNonVoid(entry.value); |
checkAssignable(entry.value, valueType, mapValueType, isConst: isConst); |
} |
return mapType; |
@@ -1963,7 +1982,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
// found in the enclosing scope nor through lookup on 'this' or |
// [: x.foo(b: 42); :] where 'foo' cannot be not found through lookup on |
// the static type of 'x'. |
- return analyze(node.expression); |
+ return analyzeNonVoid(node.expression); |
} |
visitSwitchStatement(SwitchStatement node) { |
@@ -1971,7 +1990,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
// switch cases. |
// TODO(johnniwinther): Provide hint of duplicate case constants. |
- ResolutionDartType expressionType = analyze(node.expression); |
+ ResolutionDartType expressionType = analyzeNonVoid(node.expression); |
// Check that all the case expressions are assignable to the expression. |
bool hasDefaultCase = false; |
@@ -1983,7 +2002,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> { |
CaseMatch caseMatch = labelOrCase.asCaseMatch(); |
if (caseMatch == null) continue; |
- ResolutionDartType caseType = analyze(caseMatch.expression); |
+ ResolutionDartType caseType = analyzeNonVoid(caseMatch.expression); |
checkAssignable(caseMatch, expressionType, caseType); |
} |