Skip to content

Commit b6a679c

Browse files
committed
implement else on loops and break can give an expression
closes #357
1 parent 0454e61 commit b6a679c

File tree

6 files changed

+217
-42
lines changed

6 files changed

+217
-42
lines changed

doc/langref.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ TypeExpr = PrefixOpExpression | "var"
4545
4646
BlockOrExpression = Block | Expression
4747
48-
Expression = ReturnExpression | AssignmentExpression
48+
Expression = ReturnExpression | BreakExpression | AssignmentExpression
4949
5050
AsmExpression = "asm" option("volatile") "(" String option(AsmOutput) ")"
5151
@@ -79,12 +79,14 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbo
7979
8080
SwitchItem = Expression | (Expression "..." Expression)
8181
82-
ForExpression(body) = "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body
82+
ForExpression(body) = "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
8383
8484
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
8585
8686
ReturnExpression = option("%") "return" option(Expression)
8787
88+
BreakExpression = "break" option(Expression)
89+
8890
Defer(body) = option("%") "defer" body
8991
9092
IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
@@ -151,7 +153,7 @@ GotoExpression = "goto" Symbol
151153
152154
GroupedExpression = "(" Expression ")"
153155
154-
KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "this" | "unreachable"
156+
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
155157
156158
ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}"
157159
```

src/all_types.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ struct AstNodeForExpr {
623623
AstNode *elem_node; // always a symbol
624624
AstNode *index_node; // always a symbol, might be null
625625
AstNode *body;
626+
AstNode *else_node; // can be null
626627
bool elem_is_ptr;
627628
bool is_inline;
628629
};
@@ -774,6 +775,7 @@ struct AstNodeBoolLiteral {
774775
};
775776

776777
struct AstNodeBreakExpr {
778+
AstNode *expr; // may be null
777779
};
778780

779781
struct AstNodeContinueExpr {

src/ast_render.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
485485
}
486486
break;
487487
}
488+
case NodeTypeBreak:
489+
{
490+
fprintf(ar->f, "break");
491+
if (node->data.break_expr.expr) {
492+
fprintf(ar->f, " ");
493+
render_node_grouped(ar, node->data.break_expr.expr);
494+
}
495+
break;
496+
}
488497
case NodeTypeDefer:
489498
{
490499
const char *defer_str = defer_string(node->data.defer.kind);
@@ -880,11 +889,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
880889
fprintf(ar->f, "| ");
881890
}
882891
render_node_grouped(ar, node->data.for_expr.body);
883-
break;
884-
}
885-
case NodeTypeBreak:
886-
{
887-
fprintf(ar->f, "break");
892+
if (node->data.for_expr.else_node) {
893+
fprintf(ar->f, " else");
894+
render_node_grouped(ar, node->data.for_expr.else_node);
895+
}
888896
break;
889897
}
890898
case NodeTypeContinue:

src/ir.cpp

Lines changed: 104 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct LoopStackItem {
2222
IrBasicBlock *break_block;
2323
IrBasicBlock *continue_block;
2424
IrInstruction *is_comptime;
25+
ZigList<IrInstruction *> *incoming_values;
26+
ZigList<IrBasicBlock *> *incoming_blocks;
2527
};
2628

2729
struct IrBuilder {
@@ -56,6 +58,18 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc
5658
static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
5759
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);
5860

61+
static LoopStackItem *add_loop_stack_item(IrBuilder *irb, IrBasicBlock *break_block, IrBasicBlock *continue_block,
62+
IrInstruction *is_comptime, ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values)
63+
{
64+
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
65+
loop_stack_item->break_block = break_block;
66+
loop_stack_item->continue_block = continue_block;
67+
loop_stack_item->is_comptime = is_comptime;
68+
loop_stack_item->incoming_blocks = incoming_blocks;
69+
loop_stack_item->incoming_values = incoming_values;
70+
return loop_stack_item;
71+
}
72+
5973
ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
6074
assert(const_val->type->id == TypeTableEntryIdPointer);
6175
assert(const_val->special == ConstValSpecialStatic);
@@ -4693,6 +4707,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
46934707
return err_val_ptr;
46944708
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr);
46954709
IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val);
4710+
IrBasicBlock *after_cond_block = irb->current_basic_block;
4711+
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
46964712
if (!instr_is_unreachable(is_err)) {
46974713
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
46984714
else_block, body_block, is_comptime));
@@ -4706,10 +4722,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
47064722
var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value);
47074723
ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, var_value);
47084724
}
4709-
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
4710-
loop_stack_item->break_block = end_block;
4711-
loop_stack_item->continue_block = continue_block;
4712-
loop_stack_item->is_comptime = is_comptime;
4725+
ZigList<IrInstruction *> incoming_values = {0};
4726+
ZigList<IrBasicBlock *> incoming_blocks = {0};
4727+
add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
47134728
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, payload_scope);
47144729
if (body_result == irb->codegen->invalid_instruction)
47154730
return body_result;
@@ -4727,6 +4742,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
47274742
ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime));
47284743
}
47294744

4745+
IrInstruction *else_result = nullptr;
47304746
if (else_node) {
47314747
ir_set_cursor_at_end(irb, else_block);
47324748

@@ -4738,15 +4754,23 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
47384754
IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr);
47394755
ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, err_var_value);
47404756

4741-
IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope);
4757+
else_result = ir_gen_node(irb, else_node, err_scope);
47424758
if (else_result == irb->codegen->invalid_instruction)
47434759
return else_result;
47444760
if (!instr_is_unreachable(else_result))
47454761
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
47464762
}
4747-
4763+
IrBasicBlock *after_else_block = irb->current_basic_block;
47484764
ir_set_cursor_at_end(irb, end_block);
4749-
return ir_build_const_void(irb, scope, node);
4765+
if (else_result) {
4766+
incoming_blocks.append(after_else_block);
4767+
incoming_values.append(else_result);
4768+
} else {
4769+
incoming_blocks.append(after_cond_block);
4770+
incoming_values.append(void_else_result);
4771+
}
4772+
4773+
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
47504774
} else if (var_symbol != nullptr) {
47514775
ir_set_cursor_at_end(irb, cond_block);
47524776
// TODO make it an error to write to payload variable
@@ -4759,6 +4783,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
47594783
return maybe_val_ptr;
47604784
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
47614785
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val);
4786+
IrBasicBlock *after_cond_block = irb->current_basic_block;
4787+
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
47624788
if (!instr_is_unreachable(is_non_null)) {
47634789
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
47644790
body_block, else_block, is_comptime));
@@ -4769,10 +4795,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
47694795
IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
47704796
var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value);
47714797
ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, var_value);
4772-
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
4773-
loop_stack_item->break_block = end_block;
4774-
loop_stack_item->continue_block = continue_block;
4775-
loop_stack_item->is_comptime = is_comptime;
4798+
ZigList<IrInstruction *> incoming_values = {0};
4799+
ZigList<IrBasicBlock *> incoming_blocks = {0};
4800+
add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
47764801
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, child_scope);
47774802
if (body_result == irb->codegen->invalid_instruction)
47784803
return body_result;
@@ -4790,18 +4815,27 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
47904815
ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime));
47914816
}
47924817

4818+
IrInstruction *else_result = nullptr;
47934819
if (else_node) {
47944820
ir_set_cursor_at_end(irb, else_block);
47954821

4796-
IrInstruction *else_result = ir_gen_node(irb, else_node, scope);
4822+
else_result = ir_gen_node(irb, else_node, scope);
47974823
if (else_result == irb->codegen->invalid_instruction)
47984824
return else_result;
47994825
if (!instr_is_unreachable(else_result))
48004826
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
48014827
}
4802-
4828+
IrBasicBlock *after_else_block = irb->current_basic_block;
48034829
ir_set_cursor_at_end(irb, end_block);
4804-
return ir_build_const_void(irb, scope, node);
4830+
if (else_result) {
4831+
incoming_blocks.append(after_else_block);
4832+
incoming_values.append(else_result);
4833+
} else {
4834+
incoming_blocks.append(after_cond_block);
4835+
incoming_values.append(void_else_result);
4836+
}
4837+
4838+
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
48054839
} else {
48064840
if (continue_expr_node) {
48074841
ir_set_cursor_at_end(irb, continue_block);
@@ -4816,17 +4850,18 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
48164850
IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
48174851
if (cond_val == irb->codegen->invalid_instruction)
48184852
return cond_val;
4853+
IrBasicBlock *after_cond_block = irb->current_basic_block;
4854+
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
48194855
if (!instr_is_unreachable(cond_val)) {
48204856
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
48214857
body_block, else_block, is_comptime));
48224858
}
48234859

48244860
ir_set_cursor_at_end(irb, body_block);
48254861

4826-
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
4827-
loop_stack_item->break_block = end_block;
4828-
loop_stack_item->continue_block = continue_block;
4829-
loop_stack_item->is_comptime = is_comptime;
4862+
ZigList<IrInstruction *> incoming_values = {0};
4863+
ZigList<IrBasicBlock *> incoming_blocks = {0};
4864+
add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
48304865
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, scope);
48314866
if (body_result == irb->codegen->invalid_instruction)
48324867
return body_result;
@@ -4835,19 +4870,27 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
48354870
if (!instr_is_unreachable(body_result))
48364871
ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
48374872

4873+
IrInstruction *else_result = nullptr;
48384874
if (else_node) {
48394875
ir_set_cursor_at_end(irb, else_block);
48404876

4841-
IrInstruction *else_result = ir_gen_node(irb, else_node, scope);
4877+
else_result = ir_gen_node(irb, else_node, scope);
48424878
if (else_result == irb->codegen->invalid_instruction)
48434879
return else_result;
48444880
if (!instr_is_unreachable(else_result))
48454881
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
48464882
}
4847-
4883+
IrBasicBlock *after_else_block = irb->current_basic_block;
48484884
ir_set_cursor_at_end(irb, end_block);
4885+
if (else_result) {
4886+
incoming_blocks.append(after_else_block);
4887+
incoming_values.append(else_result);
4888+
} else {
4889+
incoming_blocks.append(after_cond_block);
4890+
incoming_values.append(void_else_result);
4891+
}
48494892

4850-
return ir_build_const_void(irb, scope, node);
4893+
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
48514894
}
48524895
}
48534896

@@ -4858,6 +4901,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
48584901
AstNode *elem_node = node->data.for_expr.elem_node;
48594902
AstNode *index_node = node->data.for_expr.index_node;
48604903
AstNode *body_node = node->data.for_expr.body;
4904+
AstNode *else_node = node->data.for_expr.else_node;
48614905

48624906
if (!elem_node) {
48634907
add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter"));
@@ -4915,6 +4959,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
49154959
IrBasicBlock *cond_block = ir_build_basic_block(irb, child_scope, "ForCond");
49164960
IrBasicBlock *body_block = ir_build_basic_block(irb, child_scope, "ForBody");
49174961
IrBasicBlock *end_block = ir_build_basic_block(irb, child_scope, "ForEnd");
4962+
IrBasicBlock *else_block = else_node ? ir_build_basic_block(irb, child_scope, "ForElse") : end_block;
49184963
IrBasicBlock *continue_block = ir_build_basic_block(irb, child_scope, "ForContinue");
49194964

49204965
IrInstruction *len_val = ir_build_array_len(irb, child_scope, node, array_val);
@@ -4923,7 +4968,9 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
49234968
ir_set_cursor_at_end(irb, cond_block);
49244969
IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr);
49254970
IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false);
4926-
ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, end_block, is_comptime));
4971+
IrBasicBlock *after_cond_block = irb->current_basic_block;
4972+
IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
4973+
ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime));
49274974

49284975
ir_set_cursor_at_end(irb, body_block);
49294976
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false);
@@ -4935,10 +4982,9 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
49354982
}
49364983
ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val));
49374984

4938-
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
4939-
loop_stack_item->break_block = end_block;
4940-
loop_stack_item->continue_block = continue_block;
4941-
loop_stack_item->is_comptime = is_comptime;
4985+
ZigList<IrInstruction *> incoming_values = {0};
4986+
ZigList<IrBasicBlock *> incoming_blocks = {0};
4987+
add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
49424988
IrInstruction *body_result = ir_gen_node(irb, body_node, child_scope);
49434989
irb->loop_stack.pop();
49444990

@@ -4950,9 +4996,28 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
49504996
ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val));
49514997
ir_build_br(irb, child_scope, node, cond_block, is_comptime);
49524998

4999+
IrInstruction *else_result = nullptr;
5000+
if (else_node) {
5001+
ir_set_cursor_at_end(irb, else_block);
5002+
5003+
else_result = ir_gen_node(irb, else_node, parent_scope);
5004+
if (else_result == irb->codegen->invalid_instruction)
5005+
return else_result;
5006+
if (!instr_is_unreachable(else_result))
5007+
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
5008+
}
5009+
IrBasicBlock *after_else_block = irb->current_basic_block;
49535010
ir_set_cursor_at_end(irb, end_block);
4954-
return ir_build_const_void(irb, child_scope, node);
49555011

5012+
if (else_result) {
5013+
incoming_blocks.append(after_else_block);
5014+
incoming_values.append(else_result);
5015+
} else {
5016+
incoming_blocks.append(after_cond_block);
5017+
incoming_values.append(void_else_value);
5018+
}
5019+
5020+
return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
49565021
}
49575022

49585023
static IrInstruction *ir_gen_this_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
@@ -5512,8 +5577,20 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node)
55125577
is_comptime = loop_stack_item->is_comptime;
55135578
}
55145579

5580+
IrInstruction *result_value;
5581+
if (node->data.break_expr.expr) {
5582+
result_value = ir_gen_node(irb, node->data.break_expr.expr, scope);
5583+
if (result_value == irb->codegen->invalid_instruction)
5584+
return irb->codegen->invalid_instruction;
5585+
} else {
5586+
result_value = ir_build_const_void(irb, scope, node);
5587+
}
5588+
55155589
IrBasicBlock *dest_block = loop_stack_item->break_block;
55165590
ir_gen_defers_for_block(irb, scope, dest_block->scope, false);
5591+
5592+
loop_stack_item->incoming_blocks->append(irb->current_basic_block);
5593+
loop_stack_item->incoming_values->append(result_value);
55175594
return ir_build_br(irb, scope, node, dest_block, is_comptime);
55185595
}
55195596

0 commit comments

Comments
 (0)