Commit 356424916c

Josh Wolfe <thejoshwolfe@gmail.com>
2017-04-13 07:18:56
block statement lists never get fake expressions
instead blocks have a field that encodes whether the last statement ended with a semicolon.
1 parent 9199103
src/all_types.hpp
@@ -410,10 +410,8 @@ struct AstNodeParamDecl {
 };
 
 struct AstNodeBlock {
-    // the final statement is the returned expression.
-    // if there are no statements, the returned expression is void.
-    // the final statement is never a label.
     ZigList<AstNode *> statements;
+    bool last_statement_is_result_expression;
 };
 
 enum ReturnKind {
src/ast_render.cpp
@@ -464,8 +464,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 }
                 print_indent(ar);
                 render_node_grouped(ar, statement);
-                if (i != node->data.block.statements.length - 1)
+                if (!(i == node->data.block.statements.length - 1 &&
+                      node->data.block.last_statement_is_result_expression)) {
                     fprintf(ar->f, ";");
+                }
                 fprintf(ar->f, "\n");
             }
             ar->indent -= ar->indent_size;
src/ir.cpp
@@ -3378,6 +3378,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
 
             // a label is an entry point
             is_continuation_unreachable = false;
+            return_value = nullptr;
             continue;
         }
 
@@ -3406,7 +3407,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
             child_scope = decl_var_instruction->var->child_scope;
         } else {
             // label, defer, variable declaration will never be the last statement
-            if (i == block_node->data.block.statements.length - 1) {
+            if (block_node->data.block.last_statement_is_result_expression &&
+                i == block_node->data.block.statements.length - 1) {
                 // this is the result value statement
                 return_value = statement_value;
             } else {
@@ -3422,13 +3424,18 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
         }
     }
 
-    assert(return_value != nullptr);
-
     if (!is_continuation_unreachable) {
         // control flow falls out of block
+
+        if (!block_node->data.block.last_statement_is_result_expression) {
+            assert(return_value == nullptr);
+            return_value = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
+        }
+
         ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false, false);
     }
 
+    assert(return_value != nullptr);
     return return_value;
 }
 
src/parser.cpp
@@ -1882,36 +1882,6 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo
     }
 }
 
-static bool statement_has_block_body(AstNode *node) {
-    switch (node->type) {
-        case NodeTypeIfBoolExpr:
-            if (node->data.if_bool_expr.else_node)
-                return statement_has_block_body(node->data.if_bool_expr.else_node);
-            return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
-        case NodeTypeIfVarExpr:
-            if (node->data.if_var_expr.else_node)
-                return statement_has_block_body(node->data.if_var_expr.else_node);
-            return node->data.if_var_expr.then_block->type == NodeTypeBlock;
-        case NodeTypeTryExpr:
-            if (node->data.try_expr.else_node)
-                return statement_has_block_body(node->data.try_expr.else_node);
-            return node->data.try_expr.then_node->type == NodeTypeBlock;
-        case NodeTypeWhileExpr:
-            return node->data.while_expr.body->type == NodeTypeBlock;
-        case NodeTypeForExpr:
-            return node->data.for_expr.body->type == NodeTypeBlock;
-        case NodeTypeSwitchExpr:
-        case NodeTypeBlock:
-            return true;
-        case NodeTypeCompTime:
-            return node->data.comptime_expr.expr->type == NodeTypeBlock;
-        case NodeTypeDefer:
-            return node->data.defer.expr->type == NodeTypeBlock;
-        default:
-            return false;
-    }
-}
-
 /*
 BlockExpression(body) = Block | IfExpression(body) | TryExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body)
 */
@@ -2124,9 +2094,35 @@ static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mand
     return node;
 }
 
-static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) {
-    AstNode *node = ast_create_node(pc, NodeTypeBlock, token);
-    return node;
+static bool statement_terminates_without_semicolon(AstNode *node) {
+    switch (node->type) {
+        case NodeTypeIfBoolExpr:
+            if (node->data.if_bool_expr.else_node)
+                return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node);
+            return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
+        case NodeTypeIfVarExpr:
+            if (node->data.if_var_expr.else_node)
+                return statement_terminates_without_semicolon(node->data.if_var_expr.else_node);
+            return node->data.if_var_expr.then_block->type == NodeTypeBlock;
+        case NodeTypeTryExpr:
+            if (node->data.try_expr.else_node)
+                return statement_terminates_without_semicolon(node->data.try_expr.else_node);
+            return node->data.try_expr.then_node->type == NodeTypeBlock;
+        case NodeTypeWhileExpr:
+            return node->data.while_expr.body->type == NodeTypeBlock;
+        case NodeTypeForExpr:
+            return node->data.for_expr.body->type == NodeTypeBlock;
+        case NodeTypeCompTime:
+            return node->data.comptime_expr.expr->type == NodeTypeBlock;
+        case NodeTypeDefer:
+            return node->data.defer.expr->type == NodeTypeBlock;
+        case NodeTypeSwitchExpr:
+        case NodeTypeBlock:
+        case NodeTypeLabel:
+            return true;
+        default:
+            return false;
+    }
 }
 
 /*
@@ -2149,56 +2145,36 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
 
     for (;;) {
         AstNode *statement_node = ast_parse_label(pc, token_index, false);
-        bool need_implicit_final_void_statement = false;
+        if (!statement_node)
+            statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate);
+        if (!statement_node)
+            statement_node = ast_parse_defer_expr(pc, token_index);
+        if (!statement_node)
+            statement_node = ast_parse_block_expr(pc, token_index, false);
+        if (!statement_node)
+            statement_node = ast_parse_expression(pc, token_index, false);
+
         bool semicolon_expected = true;
         if (statement_node) {
-            // label
-            semicolon_expected = false;
-            // if a label is the last thing in a block, add a void statement.
-            need_implicit_final_void_statement = true;
-        } else {
-            statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate);
-            if (!statement_node) {
-                statement_node = ast_parse_defer_expr(pc, token_index);
-                if (statement_node) {
-                    // defer
-                    if (statement_has_block_body(statement_node)) {
-                        // don't let defer be the last statement in a block
-                        need_implicit_final_void_statement = true;
-                        semicolon_expected = false;
-                    } else {
-                        // defer without a block body requires a semicolon
-                        Token *token = &pc->tokens->at(*token_index);
-                        ast_expect_token(pc, token, TokenIdSemicolon);
-                    }
-                } else {
-                    statement_node = ast_parse_block_expr(pc, token_index, false);
-                    if (statement_node) {
-                        // block expr
-                        if (statement_has_block_body(statement_node))
-                            semicolon_expected = false;
-                    } else {
-                        statement_node = ast_parse_expression(pc, token_index, false);
-                        if (!statement_node) {
-                            // no statement.
-                            // final semicolon means add a void statement.
-                            need_implicit_final_void_statement = true;
-                        }
-                    }
+            node->data.block.statements.append(statement_node);
+            if (statement_terminates_without_semicolon(statement_node)) {
+                semicolon_expected = false;
+            } else {
+                if (statement_node->type == NodeTypeDefer) {
+                    // defer without a block body requires a semicolon
+                    Token *token = &pc->tokens->at(*token_index);
+                    ast_expect_token(pc, token, TokenIdSemicolon);
                 }
             }
         }
-        if (statement_node)
-            node->data.block.statements.append(statement_node);
+
+        node->data.block.last_statement_is_result_expression = statement_node && !(
+            statement_node->type == NodeTypeLabel ||
+            statement_node->type == NodeTypeDefer);
 
         last_token = &pc->tokens->at(*token_index);
         if (last_token->id == TokenIdRBrace) {
             *token_index += 1;
-
-            if (node->data.block.statements.length > 0 && need_implicit_final_void_statement) {
-                node->data.block.statements.append(ast_create_void_expr(pc, last_token));
-            }
-
             return node;
         } else if (!semicolon_expected) {
             continue;