Skip to content

Commit d917815

Browse files
committed
explicitly return from blocks
instead of last statement being expression value closes #629
1 parent 8bc5232 commit d917815

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+1202
-1230
lines changed

doc/docgen.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ fn gen(in: &io.InStream, out: &io.OutStream) {
4949
if (err == error.EndOfStream) {
5050
return;
5151
}
52-
std.debug.panic("{}", err)
52+
std.debug.panic("{}", err);
5353
};
5454
switch (state) {
5555
State.Start => switch (byte) {

doc/langref.html.in

+2-3
Original file line numberDiff line numberDiff line change
@@ -3021,14 +3021,13 @@ const assert = @import("std").debug.assert;</code></pre>
30213021
<pre><code class="zig">const assert = @import("std").debug.assert;
30223022

30233023
// Functions are declared like this
3024-
// The last expression in the function can be used as the return value.
30253024
fn add(a: i8, b: i8) -&gt; i8 {
30263025
if (a == 0) {
30273026
// You can still return manually if needed.
30283027
return b;
30293028
}
30303029

3031-
a + b
3030+
return a + b;
30323031
}
30333032

30343033
// The export specifier makes a function externally visible in the generated
@@ -5847,7 +5846,7 @@ ParamDeclList = "(" list(ParamDecl, ",") ")"
58475846

58485847
ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...")
58495848

5850-
Block = option(Symbol ":") "{" many(Statement) option(Expression) "}"
5849+
Block = option(Symbol ":") "{" many(Statement) "}"
58515850

58525851
Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
58535852

example/shared_library/mathtest.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export fn add(a: i32, b: i32) -> i32 {
2-
a + b
2+
return a + b;
33
}

src-self-hosted/parser.zig

+12-12
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ pub const Parser = struct {
111111
}
112112

113113
pub fn parse(self: &Parser) -> %&ast.NodeRoot {
114-
const result = self.parseInner() %% |err| {
114+
const result = self.parseInner() %% |err| x: {
115115
if (self.cleanup_root_node) |root_node| {
116116
self.freeAst(root_node);
117117
}
118-
err
118+
break :x err;
119119
};
120120
self.cleanup_root_node = null;
121121
return result;
@@ -125,12 +125,12 @@ pub const Parser = struct {
125125
var stack = self.initUtilityArrayList(State);
126126
defer self.deinitUtilityArrayList(stack);
127127

128-
const root_node = {
128+
const root_node = x: {
129129
const root_node = %return self.createRoot();
130130
%defer self.allocator.destroy(root_node);
131131
// This stack append has to succeed for freeAst to work
132132
%return stack.append(State.TopLevel);
133-
root_node
133+
break :x root_node;
134134
};
135135
assert(self.cleanup_root_node == null);
136136
self.cleanup_root_node = root_node;
@@ -462,7 +462,7 @@ pub const Parser = struct {
462462
} else if (token.id == Token.Id.Keyword_noalias) {
463463
param_decl.noalias_token = token;
464464
token = self.getNextToken();
465-
};
465+
}
466466
if (token.id == Token.Id.Identifier) {
467467
const next_token = self.getNextToken();
468468
if (next_token.id == Token.Id.Colon) {
@@ -793,14 +793,14 @@ pub const Parser = struct {
793793
}
794794

795795
fn getNextToken(self: &Parser) -> Token {
796-
return if (self.put_back_count != 0) {
796+
if (self.put_back_count != 0) {
797797
const put_back_index = self.put_back_count - 1;
798798
const put_back_token = self.put_back_tokens[put_back_index];
799799
self.put_back_count = put_back_index;
800-
put_back_token
800+
return put_back_token;
801801
} else {
802-
self.tokenizer.next()
803-
};
802+
return self.tokenizer.next();
803+
}
804804
}
805805

806806
const RenderAstFrame = struct {
@@ -873,7 +873,7 @@ pub const Parser = struct {
873873
Token.Id.Keyword_pub => %return stream.print("pub "),
874874
Token.Id.Keyword_export => %return stream.print("export "),
875875
else => unreachable,
876-
};
876+
}
877877
}
878878
if (fn_proto.extern_token) |extern_token| {
879879
%return stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
@@ -1102,7 +1102,7 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) -> %[]u8 {
11021102
// TODO test for memory leaks
11031103
// TODO test for valid frees
11041104
fn testCanonical(source: []const u8) {
1105-
const needed_alloc_count = {
1105+
const needed_alloc_count = x: {
11061106
// Try it once with unlimited memory, make sure it works
11071107
var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
11081108
var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
@@ -1116,7 +1116,7 @@ fn testCanonical(source: []const u8) {
11161116
@panic("test failed");
11171117
}
11181118
failing_allocator.allocator.free(result_source);
1119-
failing_allocator.index
1119+
break :x failing_allocator.index;
11201120
};
11211121

11221122
// TODO make this pass

src/all_types.hpp

+6-10
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ struct ScopeFnDef;
2626
struct TypeTableEntry;
2727
struct VariableTableEntry;
2828
struct ErrorTableEntry;
29-
struct LabelTableEntry;
3029
struct BuiltinFnEntry;
3130
struct TypeStructField;
3231
struct CodeGen;
@@ -54,7 +53,6 @@ struct IrExecutable {
5453
size_t *backward_branch_count;
5554
size_t backward_branch_quota;
5655
bool invalid;
57-
ZigList<LabelTableEntry *> all_labels;
5856
ZigList<IrGotoItem> goto_list;
5957
bool is_inline;
6058
FnTableEntry *fn_entry;
@@ -452,7 +450,6 @@ struct AstNodeParamDecl {
452450
struct AstNodeBlock {
453451
Buf *name;
454452
ZigList<AstNode *> statements;
455-
bool last_statement_is_result_expression;
456453
};
457454

458455
enum ReturnKind {
@@ -1644,12 +1641,6 @@ struct ErrorTableEntry {
16441641
ConstExprValue *cached_error_name_val;
16451642
};
16461643

1647-
struct LabelTableEntry {
1648-
AstNode *decl_node;
1649-
IrBasicBlock *bb;
1650-
bool used;
1651-
};
1652-
16531644
enum ScopeId {
16541645
ScopeIdDecls,
16551646
ScopeIdBlock,
@@ -1693,7 +1684,12 @@ struct ScopeDecls {
16931684
struct ScopeBlock {
16941685
Scope base;
16951686

1696-
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
1687+
Buf *name;
1688+
IrBasicBlock *end_block;
1689+
IrInstruction *is_comptime;
1690+
ZigList<IrInstruction *> *incoming_values;
1691+
ZigList<IrBasicBlock *> *incoming_blocks;
1692+
16971693
bool safety_off;
16981694
AstNode *safety_set_node;
16991695
bool fast_math_off;

src/analyze.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ ScopeBlock *create_block_scope(AstNode *node, Scope *parent) {
110110
assert(node->type == NodeTypeBlock);
111111
ScopeBlock *scope = allocate<ScopeBlock>(1);
112112
init_scope(&scope->base, ScopeIdBlock, node, parent);
113-
scope->label_table.init(1);
113+
scope->name = node->data.block.name;
114114
return scope;
115115
}
116116

src/ast_render.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -478,10 +478,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
478478
AstNode *statement = node->data.block.statements.at(i);
479479
print_indent(ar);
480480
render_node_grouped(ar, statement);
481-
if (!(i == node->data.block.statements.length - 1 &&
482-
node->data.block.last_statement_is_result_expression)) {
483-
fprintf(ar->f, ";");
484-
}
481+
fprintf(ar->f, ";");
485482
fprintf(ar->f, "\n");
486483
}
487484
ar->indent -= ar->indent_size;

src/ir.cpp

+64-32
Original file line numberDiff line numberDiff line change
@@ -3514,7 +3514,11 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s
35143514
static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {
35153515
assert(block_node->type == NodeTypeBlock);
35163516

3517+
ZigList<IrInstruction *> incoming_values = {0};
3518+
ZigList<IrBasicBlock *> incoming_blocks = {0};
3519+
35173520
ScopeBlock *scope_block = create_block_scope(block_node, parent_scope);
3521+
35183522
Scope *outer_block_scope = &scope_block->base;
35193523
Scope *child_scope = outer_block_scope;
35203524

@@ -3528,9 +3532,15 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
35283532
return ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
35293533
}
35303534

3535+
if (block_node->data.block.name != nullptr) {
3536+
scope_block->incoming_blocks = &incoming_blocks;
3537+
scope_block->incoming_values = &incoming_values;
3538+
scope_block->end_block = ir_build_basic_block(irb, parent_scope, "BlockEnd");
3539+
scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope));
3540+
}
3541+
35313542
bool is_continuation_unreachable = false;
35323543
IrInstruction *noreturn_return_value = nullptr;
3533-
IrInstruction *return_value = nullptr;
35343544
for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
35353545
AstNode *statement_node = block_node->data.block.statements.at(i);
35363546

@@ -3548,39 +3558,31 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
35483558
// variable declarations start a new scope
35493559
IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)statement_value;
35503560
child_scope = decl_var_instruction->var->child_scope;
3551-
} else {
3552-
// label, defer, variable declaration will never be the result expression
3553-
if (block_node->data.block.last_statement_is_result_expression &&
3554-
i == block_node->data.block.statements.length - 1) {
3555-
// this is the result value statement
3556-
return_value = statement_value;
3557-
} else {
3558-
// there are more statements ahead of this one. this statement's value must be void
3559-
if (statement_value != irb->codegen->invalid_instruction) {
3560-
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
3561-
}
3562-
}
3561+
} else if (statement_value != irb->codegen->invalid_instruction) {
3562+
// this statement's value must be void
3563+
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
35633564
}
35643565
}
35653566

35663567
if (is_continuation_unreachable) {
35673568
assert(noreturn_return_value != nullptr);
3568-
return noreturn_return_value;
3569+
if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
3570+
return noreturn_return_value;
3571+
}
3572+
} else {
3573+
incoming_blocks.append(irb->current_basic_block);
3574+
incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));
35693575
}
3570-
// control flow falls out of block
35713576

3572-
if (block_node->data.block.last_statement_is_result_expression) {
3573-
// return value was determined by the last statement
3574-
assert(return_value != nullptr);
3577+
if (block_node->data.block.name != nullptr) {
3578+
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
3579+
ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime));
3580+
ir_set_cursor_at_end(irb, scope_block->end_block);
3581+
return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
35753582
} else {
3576-
// return value is implicitly void
3577-
assert(return_value == nullptr);
3578-
return_value = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
3583+
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
3584+
return ir_mark_gen(ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)));
35793585
}
3580-
3581-
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
3582-
3583-
return return_value;
35843586
}
35853587

35863588
static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
@@ -5952,21 +5954,46 @@ static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNo
59525954
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval);
59535955
}
59545956

5957+
static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) {
5958+
IrInstruction *is_comptime;
5959+
if (ir_should_inline(irb->exec, break_scope)) {
5960+
is_comptime = ir_build_const_bool(irb, break_scope, node, true);
5961+
} else {
5962+
is_comptime = block_scope->is_comptime;
5963+
}
5964+
5965+
IrInstruction *result_value;
5966+
if (node->data.break_expr.expr) {
5967+
result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope);
5968+
if (result_value == irb->codegen->invalid_instruction)
5969+
return irb->codegen->invalid_instruction;
5970+
} else {
5971+
result_value = ir_build_const_void(irb, break_scope, node);
5972+
}
5973+
5974+
IrBasicBlock *dest_block = block_scope->end_block;
5975+
ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
5976+
5977+
block_scope->incoming_blocks->append(irb->current_basic_block);
5978+
block_scope->incoming_values->append(result_value);
5979+
return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
5980+
}
5981+
59555982
static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
59565983
assert(node->type == NodeTypeBreak);
59575984

59585985
// Search up the scope. We'll find one of these things first:
59595986
// * function definition scope or global scope => error, break outside loop
59605987
// * defer expression scope => error, cannot break out of defer expression
59615988
// * loop scope => OK
5989+
// * (if it's a labeled break) labeled block => OK
59625990

59635991
Scope *search_scope = break_scope;
59645992
ScopeLoop *loop_scope;
5965-
bool saw_any_loop_scope = false;
59665993
for (;;) {
59675994
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
5968-
if (saw_any_loop_scope) {
5969-
add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.break_expr.name)));
5995+
if (node->data.break_expr.name != nullptr) {
5996+
add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name)));
59705997
return irb->codegen->invalid_instruction;
59715998
} else {
59725999
add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
@@ -5977,13 +6004,20 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
59776004
return irb->codegen->invalid_instruction;
59786005
} else if (search_scope->id == ScopeIdLoop) {
59796006
ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
5980-
saw_any_loop_scope = true;
59816007
if (node->data.break_expr.name == nullptr ||
59826008
(this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
59836009
{
59846010
loop_scope = this_loop_scope;
59856011
break;
59866012
}
6013+
} else if (search_scope->id == ScopeIdBlock) {
6014+
ScopeBlock *this_block_scope = (ScopeBlock *)search_scope;
6015+
if (node->data.break_expr.name != nullptr &&
6016+
(this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))
6017+
{
6018+
assert(this_block_scope->end_block != nullptr);
6019+
return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
6020+
}
59876021
}
59886022
search_scope = search_scope->parent;
59896023
}
@@ -6022,10 +6056,9 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
60226056

60236057
Scope *search_scope = continue_scope;
60246058
ScopeLoop *loop_scope;
6025-
bool saw_any_loop_scope = false;
60266059
for (;;) {
60276060
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
6028-
if (saw_any_loop_scope) {
6061+
if (node->data.continue_expr.name != nullptr) {
60296062
add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name)));
60306063
return irb->codegen->invalid_instruction;
60316064
} else {
@@ -6037,7 +6070,6 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
60376070
return irb->codegen->invalid_instruction;
60386071
} else if (search_scope->id == ScopeIdLoop) {
60396072
ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
6040-
saw_any_loop_scope = true;
60416073
if (node->data.continue_expr.name == nullptr ||
60426074
(this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
60436075
{

0 commit comments

Comments
 (0)