diff -r 6030e0b46fe2 -r 2b8512340180 src/compiler/SemanticAnalysis.cpp --- a/src/compiler/SemanticAnalysis.cpp Sat Dec 22 18:25:31 2012 -0800 +++ b/src/compiler/SemanticAnalysis.cpp Sat Dec 22 19:48:43 2012 -0800 @@ -79,7 +79,8 @@ emitter_(pool_), module_(parent->module_), fun_(fun), - loop_(NULL) + loop_(NULL), + control_(NULL) { } @@ -167,8 +168,21 @@ } AutoEnterScope varScope(&scope_, stmt->varScope()); + ControlStatus status = visitForControl(stmt->body()); - stmt->body()->accept(this); + if (!fun_->returnType()->isVoid()) { + // If the function is not void, report if not all paths return a value. + // All other incorrect return cases have been caught already. + if (status.returns() == Returns_Sometimes) { + cc_.reportError(stmt->pos(), Message_NotAllPathsReturnValue); + return NULL; + } + + if (status.returns() == Returns_Never) { + cc_.reportError(stmt->pos(), Message_MissingReturnValue); + return NULL; + } + } Local code(zone_, __ finish()); if (!code) @@ -217,28 +231,60 @@ } void +SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right) +{ + // If both sides terminate in a return, we consider this a return as well. + if (left.returns() == Returns_Always && right.returns() == Returns_Always) { + terminate(Returns_Always); + return; + } + + ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never) + ? Returns_Never + : Returns_Sometimes; + + if (left.terminates() && right.terminates()) + terminate(status); + else + returns(status); +} + +SemanticAnalysis::ControlStatus +SemanticAnalysis::visitForControl(Statement *statement) +{ + ControlScope scope(&control_); + statement->accept(this); + return scope.status(); +} + +void SemanticAnalysis::visitIfStatement(IfStatement *node) { Label join; + ControlStatus trueBranch; + ControlStatus falseBranch(false, Returns_Never); + if (!node->ifFalse()) { Label success; visitForTest(node->condition(), &success, &join, &success); __ bind(&success); - node->ifTrue()->accept(this); + trueBranch = visitForControl(node->ifTrue()); } else { Label success, next; visitForTest(node->condition(), &success, &next, &success); __ bind(&success); - node->ifTrue()->accept(this); + trueBranch = visitForControl(node->ifTrue()); __ jump(&join); __ bind(&next); - node->ifFalse()->accept(this); + falseBranch = visitForControl(node->ifFalse()); } __ bind(&join); + + control_->merge(trueBranch, falseBranch); } void @@ -250,7 +296,7 @@ loop_->setUsesLifoStack(); } -void +SemanticAnalysis::ControlStatus SemanticAnalysis::visitLoopBody(Statement *stmt) { // We always mark off a loop body, so on each edge, we can balance the @@ -264,11 +310,13 @@ // possibility that a long-running loop will overflow the LIFO region // and therefore start requiring nursery allocations. int pos = __ enterloop(); - stmt->accept(this); + ControlStatus status = visitForControl(stmt); __ leaveloop(); if (loop_->usesLifoStack()) __ setLoopUsesLifoStack(pos); + + return status; } void @@ -289,7 +337,7 @@ __ jump(&test); __ bind(&header); - visitLoopBody(node->body()); + ControlStatus status = visitLoopBody(node->body()); __ bind(&update); if (node->update()) @@ -303,6 +351,14 @@ } __ bind(&join); + + // If there was a condition, we treat this like a while loop. Otherwise, + // the join point is only reachable if the loop contains a break. The + // termination status doesn't tell us anything yet. + if (node->condition() || loop.breaks()) + control_->merge(status, ControlStatus(false, Returns_Never)); + else + control_->terminate(status.returns()); assert(__ stackDepth() == 0); } @@ -317,19 +373,34 @@ __ bind(&header); + ControlStatus status; + LoopScope loop(scope_, &join, &header, &loop_); if (node->token() == TOK_WHILE) { Label body; visitForTest(node->condition(), &body, &join, &body); - visitLoopBody(node->body()); + status = visitLoopBody(node->body()); __ jump(&header); } else { - visitLoopBody(node->body()); + status = visitLoopBody(node->body()); visitForTest(node->condition(), &header, &join, &join); } __ bind(&join); + // Detect loops that will always have a fork in the contro-flow graph. + // This will be a |while| loop, or a |do-while| loop whose body does + // not terminate. For now, we treat these as an |if| regardless of the + // direction the branch can take. That means the control-flow is always + // conditional. + if (node->token() == TOK_WHILE || status.terminates() || loop.breaks()) { + control_->merge(status, ControlStatus(false, Returns_Never)); + } else { + // At this point, we've got a do-while loop with a |return| or + // |continue|, meaning that the rest of the block is unreachable. + control_->terminate(status.returns()); + } + assert(__ stackDepth() == 0); } @@ -398,9 +469,11 @@ void SemanticAnalysis::visitReturnStatement(ReturnStatement *node) { + control_->returns(Returns_Always); + if (!node->expression()) { if (!fun_->returnType()->isVoid()) { - cc_.reportError(node->pos(), Message_UsedVoidReturn); + cc_.reportError(node->pos(), Message_MissingReturnValue); return; } @@ -408,6 +481,11 @@ return; } + if (fun_->returnType()->isVoid()) { + cc_.reportError(node->pos(), Message_UsedVoidReturn); + return; + } + PoolScope poolScope(zone_->pool()); RootScope rootScope(zone_); @@ -420,7 +498,11 @@ return; discharge(hir); - __ return_(); + + if (hir->type()->isVoid()) + __ returnvoid(); + else + __ return_(); } void @@ -601,6 +683,7 @@ return; } + loop_->setBreaks(); __ j(OP_BREAK, loop_->break_()); }