Chromium Code Reviews

Unified Diff: pkg/compiler/lib/src/typechecker.dart

Issue 2699073003: Support `void` as generic argument.
Patch Set: Shuffle things around to have a better `voidRti` locality. Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
« no previous file with comments | « pkg/compiler/lib/src/resolution/signatures.dart ('k') | pkg/front_end/lib/src/fasta/parser/parser.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
« no previous file with comments | « pkg/compiler/lib/src/resolution/signatures.dart ('k') | pkg/front_end/lib/src/fasta/parser/parser.dart » ('j') | no next file with comments »

Powered by Google App Engine