Skip to content

Commit 3564249

Browse files
committed
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 commit 3564249

File tree

4 files changed

+64
-81
lines changed

4 files changed

+64
-81
lines changed

src/all_types.hpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,10 +410,8 @@ struct AstNodeParamDecl {
410410
};
411411

412412
struct AstNodeBlock {
413-
// the final statement is the returned expression.
414-
// if there are no statements, the returned expression is void.
415-
// the final statement is never a label.
416413
ZigList<AstNode *> statements;
414+
bool last_statement_is_result_expression;
417415
};
418416

419417
enum ReturnKind {

src/ast_render.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
464464
}
465465
print_indent(ar);
466466
render_node_grouped(ar, statement);
467-
if (i != node->data.block.statements.length - 1)
467+
if (!(i == node->data.block.statements.length - 1 &&
468+
node->data.block.last_statement_is_result_expression)) {
468469
fprintf(ar->f, ";");
470+
}
469471
fprintf(ar->f, "\n");
470472
}
471473
ar->indent -= ar->indent_size;

src/ir.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3378,6 +3378,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
33783378

33793379
// a label is an entry point
33803380
is_continuation_unreachable = false;
3381+
return_value = nullptr;
33813382
continue;
33823383
}
33833384

@@ -3406,7 +3407,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
34063407
child_scope = decl_var_instruction->var->child_scope;
34073408
} else {
34083409
// label, defer, variable declaration will never be the last statement
3409-
if (i == block_node->data.block.statements.length - 1) {
3410+
if (block_node->data.block.last_statement_is_result_expression &&
3411+
i == block_node->data.block.statements.length - 1) {
34103412
// this is the result value statement
34113413
return_value = statement_value;
34123414
} else {
@@ -3422,13 +3424,18 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
34223424
}
34233425
}
34243426

3425-
assert(return_value != nullptr);
3426-
34273427
if (!is_continuation_unreachable) {
34283428
// control flow falls out of block
3429+
3430+
if (!block_node->data.block.last_statement_is_result_expression) {
3431+
assert(return_value == nullptr);
3432+
return_value = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
3433+
}
3434+
34293435
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false, false);
34303436
}
34313437

3438+
assert(return_value != nullptr);
34323439
return return_value;
34333440
}
34343441

src/parser.cpp

Lines changed: 50 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,36 +1882,6 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo
18821882
}
18831883
}
18841884

1885-
static bool statement_has_block_body(AstNode *node) {
1886-
switch (node->type) {
1887-
case NodeTypeIfBoolExpr:
1888-
if (node->data.if_bool_expr.else_node)
1889-
return statement_has_block_body(node->data.if_bool_expr.else_node);
1890-
return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
1891-
case NodeTypeIfVarExpr:
1892-
if (node->data.if_var_expr.else_node)
1893-
return statement_has_block_body(node->data.if_var_expr.else_node);
1894-
return node->data.if_var_expr.then_block->type == NodeTypeBlock;
1895-
case NodeTypeTryExpr:
1896-
if (node->data.try_expr.else_node)
1897-
return statement_has_block_body(node->data.try_expr.else_node);
1898-
return node->data.try_expr.then_node->type == NodeTypeBlock;
1899-
case NodeTypeWhileExpr:
1900-
return node->data.while_expr.body->type == NodeTypeBlock;
1901-
case NodeTypeForExpr:
1902-
return node->data.for_expr.body->type == NodeTypeBlock;
1903-
case NodeTypeSwitchExpr:
1904-
case NodeTypeBlock:
1905-
return true;
1906-
case NodeTypeCompTime:
1907-
return node->data.comptime_expr.expr->type == NodeTypeBlock;
1908-
case NodeTypeDefer:
1909-
return node->data.defer.expr->type == NodeTypeBlock;
1910-
default:
1911-
return false;
1912-
}
1913-
}
1914-
19151885
/*
19161886
BlockExpression(body) = Block | IfExpression(body) | TryExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body)
19171887
*/
@@ -2124,9 +2094,35 @@ static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mand
21242094
return node;
21252095
}
21262096

2127-
static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) {
2128-
AstNode *node = ast_create_node(pc, NodeTypeBlock, token);
2129-
return node;
2097+
static bool statement_terminates_without_semicolon(AstNode *node) {
2098+
switch (node->type) {
2099+
case NodeTypeIfBoolExpr:
2100+
if (node->data.if_bool_expr.else_node)
2101+
return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node);
2102+
return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
2103+
case NodeTypeIfVarExpr:
2104+
if (node->data.if_var_expr.else_node)
2105+
return statement_terminates_without_semicolon(node->data.if_var_expr.else_node);
2106+
return node->data.if_var_expr.then_block->type == NodeTypeBlock;
2107+
case NodeTypeTryExpr:
2108+
if (node->data.try_expr.else_node)
2109+
return statement_terminates_without_semicolon(node->data.try_expr.else_node);
2110+
return node->data.try_expr.then_node->type == NodeTypeBlock;
2111+
case NodeTypeWhileExpr:
2112+
return node->data.while_expr.body->type == NodeTypeBlock;
2113+
case NodeTypeForExpr:
2114+
return node->data.for_expr.body->type == NodeTypeBlock;
2115+
case NodeTypeCompTime:
2116+
return node->data.comptime_expr.expr->type == NodeTypeBlock;
2117+
case NodeTypeDefer:
2118+
return node->data.defer.expr->type == NodeTypeBlock;
2119+
case NodeTypeSwitchExpr:
2120+
case NodeTypeBlock:
2121+
case NodeTypeLabel:
2122+
return true;
2123+
default:
2124+
return false;
2125+
}
21302126
}
21312127

21322128
/*
@@ -2149,56 +2145,36 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
21492145

21502146
for (;;) {
21512147
AstNode *statement_node = ast_parse_label(pc, token_index, false);
2152-
bool need_implicit_final_void_statement = false;
2148+
if (!statement_node)
2149+
statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate);
2150+
if (!statement_node)
2151+
statement_node = ast_parse_defer_expr(pc, token_index);
2152+
if (!statement_node)
2153+
statement_node = ast_parse_block_expr(pc, token_index, false);
2154+
if (!statement_node)
2155+
statement_node = ast_parse_expression(pc, token_index, false);
2156+
21532157
bool semicolon_expected = true;
21542158
if (statement_node) {
2155-
// label
2156-
semicolon_expected = false;
2157-
// if a label is the last thing in a block, add a void statement.
2158-
need_implicit_final_void_statement = true;
2159-
} else {
2160-
statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate);
2161-
if (!statement_node) {
2162-
statement_node = ast_parse_defer_expr(pc, token_index);
2163-
if (statement_node) {
2164-
// defer
2165-
if (statement_has_block_body(statement_node)) {
2166-
// don't let defer be the last statement in a block
2167-
need_implicit_final_void_statement = true;
2168-
semicolon_expected = false;
2169-
} else {
2170-
// defer without a block body requires a semicolon
2171-
Token *token = &pc->tokens->at(*token_index);
2172-
ast_expect_token(pc, token, TokenIdSemicolon);
2173-
}
2174-
} else {
2175-
statement_node = ast_parse_block_expr(pc, token_index, false);
2176-
if (statement_node) {
2177-
// block expr
2178-
if (statement_has_block_body(statement_node))
2179-
semicolon_expected = false;
2180-
} else {
2181-
statement_node = ast_parse_expression(pc, token_index, false);
2182-
if (!statement_node) {
2183-
// no statement.
2184-
// final semicolon means add a void statement.
2185-
need_implicit_final_void_statement = true;
2186-
}
2187-
}
2159+
node->data.block.statements.append(statement_node);
2160+
if (statement_terminates_without_semicolon(statement_node)) {
2161+
semicolon_expected = false;
2162+
} else {
2163+
if (statement_node->type == NodeTypeDefer) {
2164+
// defer without a block body requires a semicolon
2165+
Token *token = &pc->tokens->at(*token_index);
2166+
ast_expect_token(pc, token, TokenIdSemicolon);
21882167
}
21892168
}
21902169
}
2191-
if (statement_node)
2192-
node->data.block.statements.append(statement_node);
2170+
2171+
node->data.block.last_statement_is_result_expression = statement_node && !(
2172+
statement_node->type == NodeTypeLabel ||
2173+
statement_node->type == NodeTypeDefer);
21932174

21942175
last_token = &pc->tokens->at(*token_index);
21952176
if (last_token->id == TokenIdRBrace) {
21962177
*token_index += 1;
2197-
2198-
if (node->data.block.statements.length > 0 && need_implicit_final_void_statement) {
2199-
node->data.block.statements.append(ast_create_void_expr(pc, last_token));
2200-
}
2201-
22022178
return node;
22032179
} else if (!semicolon_expected) {
22042180
continue;

0 commit comments

Comments
 (0)