authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-12-20 22:55:24-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-12-20 23:00:19-05:00
log8bc523219c66427951e5339550502871547f2138
tree207f1b9335a6bdd2d25a96c206430daf91087e24
parentd686113bd2b2e2207137de6ef81e515bc4a3aa07

add labeled loops, labeled break, labeled continue. remove goto

closes #346 closes #630 regression: translate-c can no longer translate switch statements. after #629 we can ressurect and modify the code to utilize arbitrarily returning from blocks.

15 files changed, 339 insertions(+), 688 deletions(-)

doc/langref.html.in+7-11
...@@ -5847,11 +5847,9 @@ ParamDeclList = "(" list(ParamDecl, ",") ")"...@@ -5847,11 +5847,9 @@ ParamDeclList = "(" list(ParamDecl, ",") ")"
58475847
5848ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...")5848ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...")
58495849
5850Block = "{" many(Statement) option(Expression) "}"5850Block = option(Symbol ":") "{" many(Statement) option(Expression) "}"
58515851
5852Statement = Label | LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"5852Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
5853
5854Label = Symbol ":"
58555853
5856TypeExpr = PrefixOpExpression | "var"5854TypeExpr = PrefixOpExpression | "var"
58575855
...@@ -5891,13 +5889,13 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=&gt;" option("|" option("*") Sy...@@ -5891,13 +5889,13 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=&gt;" option("|" option("*") Sy
58915889
5892SwitchItem = Expression | (Expression "..." Expression)5890SwitchItem = Expression | (Expression "..." Expression)
58935891
5894ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))5892ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
58955893
5896BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression5894BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
58975895
5898ReturnExpression = option("%") "return" option(Expression)5896ReturnExpression = option("%") "return" option(Expression)
58995897
5900BreakExpression = "break" option(Expression)5898BreakExpression = "break" option(":" Symbol) option(Expression)
59015899
5902Defer(body) = option("%") "defer" body5900Defer(body) = option("%") "defer" body
59035901
...@@ -5907,7 +5905,7 @@ TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|")...@@ -5907,7 +5905,7 @@ TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|")
59075905
5908TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body))5906TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body))
59095907
5910WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))5908WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
59115909
5912BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression5910BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression
59135911
...@@ -5955,15 +5953,13 @@ StructLiteralField = "." Symbol "=" Expression...@@ -5955,15 +5953,13 @@ StructLiteralField = "." Symbol "=" Expression
59555953
5956PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%"5954PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%"
59575955
5958PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl5956PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
59595957
5960ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr5958ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
59615959
5962GotoExpression = "goto" Symbol
5963
5964GroupedExpression = "(" Expression ")"5960GroupedExpression = "(" Expression ")"
59655961
5966KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"5962KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
59675963
5968ContainerDecl = option("extern" | "packed")5964ContainerDecl = option("extern" | "packed")
5969 ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))5965 ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
src/all_types.hpp+7-8
...@@ -386,8 +386,6 @@ enum NodeType {...@@ -386,8 +386,6 @@ enum NodeType {
386 NodeTypeSwitchExpr,386 NodeTypeSwitchExpr,
387 NodeTypeSwitchProng,387 NodeTypeSwitchProng,
388 NodeTypeSwitchRange,388 NodeTypeSwitchRange,
389 NodeTypeLabel,
390 NodeTypeGoto,
391 NodeTypeCompTime,389 NodeTypeCompTime,
392 NodeTypeBreak,390 NodeTypeBreak,
393 NodeTypeContinue,391 NodeTypeContinue,
...@@ -452,6 +450,7 @@ struct AstNodeParamDecl {...@@ -452,6 +450,7 @@ struct AstNodeParamDecl {
452};450};
453451
454struct AstNodeBlock {452struct AstNodeBlock {
453 Buf *name;
455 ZigList<AstNode *> statements;454 ZigList<AstNode *> statements;
456 bool last_statement_is_result_expression;455 bool last_statement_is_result_expression;
457};456};
...@@ -662,6 +661,7 @@ struct AstNodeTestExpr {...@@ -662,6 +661,7 @@ struct AstNodeTestExpr {
662};661};
663662
664struct AstNodeWhileExpr {663struct AstNodeWhileExpr {
664 Buf *name;
665 AstNode *condition;665 AstNode *condition;
666 Buf *var_symbol;666 Buf *var_symbol;
667 bool var_is_ptr;667 bool var_is_ptr;
...@@ -673,6 +673,7 @@ struct AstNodeWhileExpr {...@@ -673,6 +673,7 @@ struct AstNodeWhileExpr {
673};673};
674674
675struct AstNodeForExpr {675struct AstNodeForExpr {
676 Buf *name;
676 AstNode *array_expr;677 AstNode *array_expr;
677 AstNode *elem_node; // always a symbol678 AstNode *elem_node; // always a symbol
678 AstNode *index_node; // always a symbol, might be null679 AstNode *index_node; // always a symbol, might be null
...@@ -704,11 +705,6 @@ struct AstNodeLabel {...@@ -704,11 +705,6 @@ struct AstNodeLabel {
704 Buf *name;705 Buf *name;
705};706};
706707
707struct AstNodeGoto {
708 Buf *name;
709 bool is_inline;
710};
711
712struct AstNodeCompTime {708struct AstNodeCompTime {
713 AstNode *expr;709 AstNode *expr;
714};710};
...@@ -836,11 +832,14 @@ struct AstNodeBoolLiteral {...@@ -836,11 +832,14 @@ struct AstNodeBoolLiteral {
836};832};
837833
838struct AstNodeBreakExpr {834struct AstNodeBreakExpr {
835 Buf *name;
839 AstNode *expr; // may be null836 AstNode *expr; // may be null
840};837};
841838
842struct AstNodeContinueExpr {839struct AstNodeContinueExpr {
840 Buf *name;
843};841};
842
844struct AstNodeUnreachableExpr {843struct AstNodeUnreachableExpr {
845};844};
846845
...@@ -886,7 +885,6 @@ struct AstNode {...@@ -886,7 +885,6 @@ struct AstNode {
886 AstNodeSwitchProng switch_prong;885 AstNodeSwitchProng switch_prong;
887 AstNodeSwitchRange switch_range;886 AstNodeSwitchRange switch_range;
888 AstNodeLabel label;887 AstNodeLabel label;
889 AstNodeGoto goto_expr;
890 AstNodeCompTime comptime_expr;888 AstNodeCompTime comptime_expr;
891 AstNodeAsmExpr asm_expr;889 AstNodeAsmExpr asm_expr;
892 AstNodeFieldAccessExpr field_access_expr;890 AstNodeFieldAccessExpr field_access_expr;
...@@ -1741,6 +1739,7 @@ struct ScopeCImport {...@@ -1741,6 +1739,7 @@ struct ScopeCImport {
1741struct ScopeLoop {1739struct ScopeLoop {
1742 Scope base;1740 Scope base;
17431741
1742 Buf *name;
1744 IrBasicBlock *break_block;1743 IrBasicBlock *break_block;
1745 IrBasicBlock *continue_block;1744 IrBasicBlock *continue_block;
1746 IrInstruction *is_comptime;1745 IrInstruction *is_comptime;
src/analyze.cpp+7-3
...@@ -144,9 +144,15 @@ ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) {...@@ -144,9 +144,15 @@ ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) {
144}144}
145145
146ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {146ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
147 assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr);
148 ScopeLoop *scope = allocate<ScopeLoop>(1);147 ScopeLoop *scope = allocate<ScopeLoop>(1);
149 init_scope(&scope->base, ScopeIdLoop, node, parent);148 init_scope(&scope->base, ScopeIdLoop, node, parent);
149 if (node->type == NodeTypeWhileExpr) {
150 scope->name = node->data.while_expr.name;
151 } else if (node->type == NodeTypeForExpr) {
152 scope->name = node->data.for_expr.name;
153 } else {
154 zig_unreachable();
155 }
150 return scope;156 return scope;
151}157}
152158
...@@ -2916,8 +2922,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {...@@ -2916,8 +2922,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
2916 case NodeTypeSwitchExpr:2922 case NodeTypeSwitchExpr:
2917 case NodeTypeSwitchProng:2923 case NodeTypeSwitchProng:
2918 case NodeTypeSwitchRange:2924 case NodeTypeSwitchRange:
2919 case NodeTypeLabel:
2920 case NodeTypeGoto:
2921 case NodeTypeBreak:2925 case NodeTypeBreak:
2922 case NodeTypeContinue:2926 case NodeTypeContinue:
2923 case NodeTypeUnreachable:2927 case NodeTypeUnreachable:
src/ast_render.cpp+15-17
...@@ -215,10 +215,6 @@ static const char *node_type_str(NodeType node_type) {...@@ -215,10 +215,6 @@ static const char *node_type_str(NodeType node_type) {
215 return "SwitchProng";215 return "SwitchProng";
216 case NodeTypeSwitchRange:216 case NodeTypeSwitchRange:
217 return "SwitchRange";217 return "SwitchRange";
218 case NodeTypeLabel:
219 return "Label";
220 case NodeTypeGoto:
221 return "Goto";
222 case NodeTypeCompTime:218 case NodeTypeCompTime:
223 return "CompTime";219 return "CompTime";
224 case NodeTypeBreak:220 case NodeTypeBreak:
...@@ -391,7 +387,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -391,7 +387,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
391 switch (node->type) {387 switch (node->type) {
392 case NodeTypeSwitchProng:388 case NodeTypeSwitchProng:
393 case NodeTypeSwitchRange:389 case NodeTypeSwitchRange:
394 case NodeTypeLabel:
395 case NodeTypeStructValueField:390 case NodeTypeStructValueField:
396 zig_unreachable();391 zig_unreachable();
397 case NodeTypeRoot:392 case NodeTypeRoot:
...@@ -470,6 +465,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -470,6 +465,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
470 break;465 break;
471 }466 }
472 case NodeTypeBlock:467 case NodeTypeBlock:
468 if (node->data.block.name != nullptr) {
469 fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name));
470 }
473 if (node->data.block.statements.length == 0) {471 if (node->data.block.statements.length == 0) {
474 fprintf(ar->f, "{}");472 fprintf(ar->f, "{}");
475 break;473 break;
...@@ -478,13 +476,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -478,13 +476,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
478 ar->indent += ar->indent_size;476 ar->indent += ar->indent_size;
479 for (size_t i = 0; i < node->data.block.statements.length; i += 1) {477 for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
480 AstNode *statement = node->data.block.statements.at(i);478 AstNode *statement = node->data.block.statements.at(i);
481 if (statement->type == NodeTypeLabel) {
482 ar->indent -= ar->indent_size;
483 print_indent(ar);
484 fprintf(ar->f, "%s:\n", buf_ptr(statement->data.label.name));
485 ar->indent += ar->indent_size;
486 continue;
487 }
488 print_indent(ar);479 print_indent(ar);
489 render_node_grouped(ar, statement);480 render_node_grouped(ar, statement);
490 if (!(i == node->data.block.statements.length - 1 &&481 if (!(i == node->data.block.statements.length - 1 &&
...@@ -515,6 +506,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -515,6 +506,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
515 case NodeTypeBreak:506 case NodeTypeBreak:
516 {507 {
517 fprintf(ar->f, "break");508 fprintf(ar->f, "break");
509 if (node->data.break_expr.name != nullptr) {
510 fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name));
511 }
518 if (node->data.break_expr.expr) {512 if (node->data.break_expr.expr) {
519 fprintf(ar->f, " ");513 fprintf(ar->f, " ");
520 render_node_grouped(ar, node->data.break_expr.expr);514 render_node_grouped(ar, node->data.break_expr.expr);
...@@ -828,6 +822,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -828,6 +822,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
828 }822 }
829 case NodeTypeWhileExpr:823 case NodeTypeWhileExpr:
830 {824 {
825 if (node->data.while_expr.name != nullptr) {
826 fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name));
827 }
831 const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";828 const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";
832 fprintf(ar->f, "%swhile (", inline_str);829 fprintf(ar->f, "%swhile (", inline_str);
833 render_node_grouped(ar, node->data.while_expr.condition);830 render_node_grouped(ar, node->data.while_expr.condition);
...@@ -957,11 +954,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -957,11 +954,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
957 fprintf(ar->f, "}");954 fprintf(ar->f, "}");
958 break;955 break;
959 }956 }
960 case NodeTypeGoto:
961 {
962 fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name));
963 break;
964 }
965 case NodeTypeCompTime:957 case NodeTypeCompTime:
966 {958 {
967 fprintf(ar->f, "comptime ");959 fprintf(ar->f, "comptime ");
...@@ -970,6 +962,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -970,6 +962,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
970 }962 }
971 case NodeTypeForExpr:963 case NodeTypeForExpr:
972 {964 {
965 if (node->data.for_expr.name != nullptr) {
966 fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name));
967 }
973 const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";968 const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";
974 fprintf(ar->f, "%sfor (", inline_str);969 fprintf(ar->f, "%sfor (", inline_str);
975 render_node_grouped(ar, node->data.for_expr.array_expr);970 render_node_grouped(ar, node->data.for_expr.array_expr);
...@@ -995,6 +990,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -995,6 +990,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
995 case NodeTypeContinue:990 case NodeTypeContinue:
996 {991 {
997 fprintf(ar->f, "continue");992 fprintf(ar->f, "continue");
993 if (node->data.continue_expr.name != nullptr) {
994 fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name));
995 }
998 break;996 break;
999 }997 }
1000 case NodeTypeUnreachable:998 case NodeTypeUnreachable:
src/ir.cpp+32-151
...@@ -3511,29 +3511,6 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s...@@ -3511,29 +3511,6 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s
3511 return var;3511 return var;
3512}3512}
35133513
3514static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) {
3515 while (scope) {
3516 if (scope->id == ScopeIdBlock) {
3517 ScopeBlock *block_scope = (ScopeBlock *)scope;
3518 auto entry = block_scope->label_table.maybe_get(name);
3519 if (entry)
3520 return entry->value;
3521 }
3522 scope = scope->parent;
3523 }
3524
3525 return nullptr;
3526}
3527
3528static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) {
3529 while (scope) {
3530 if (scope->id == ScopeIdBlock)
3531 return (ScopeBlock *)scope;
3532 scope = scope->parent;
3533 }
3534 return nullptr;
3535}
3536
3537static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {3514static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {
3538 assert(block_node->type == NodeTypeBlock);3515 assert(block_node->type == NodeTypeBlock);
35393516
...@@ -3557,38 +3534,6 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode...@@ -3557,38 +3534,6 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
3557 for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {3534 for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
3558 AstNode *statement_node = block_node->data.block.statements.at(i);3535 AstNode *statement_node = block_node->data.block.statements.at(i);
35593536
3560 if (statement_node->type == NodeTypeLabel) {
3561 Buf *label_name = statement_node->data.label.name;
3562 IrBasicBlock *label_block = ir_build_basic_block(irb, child_scope, buf_ptr(label_name));
3563 LabelTableEntry *label = allocate<LabelTableEntry>(1);
3564 label->decl_node = statement_node;
3565 label->bb = label_block;
3566 irb->exec->all_labels.append(label);
3567
3568 LabelTableEntry *existing_label = find_label(irb->exec, child_scope, label_name);
3569 if (existing_label) {
3570 ErrorMsg *msg = add_node_error(irb->codegen, statement_node,
3571 buf_sprintf("duplicate label name '%s'", buf_ptr(label_name)));
3572 add_error_note(irb->codegen, msg, existing_label->decl_node, buf_sprintf("other label here"));
3573 return irb->codegen->invalid_instruction;
3574 } else {
3575 ScopeBlock *scope_block = find_block_scope(irb->exec, child_scope);
3576 scope_block->label_table.put(label_name, label);
3577 }
3578
3579 if (!is_continuation_unreachable) {
3580 // fall through into new labeled basic block
3581 IrInstruction *is_comptime = ir_mark_gen(ir_build_const_bool(irb, child_scope, statement_node,
3582 ir_should_inline(irb->exec, child_scope)));
3583 ir_mark_gen(ir_build_br(irb, child_scope, statement_node, label_block, is_comptime));
3584 }
3585 ir_set_cursor_at_end(irb, label_block);
3586
3587 // a label is an entry point
3588 is_continuation_unreachable = false;
3589 continue;
3590 }
3591
3592 IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope);3537 IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope);
3593 is_continuation_unreachable = instr_is_unreachable(statement_value);3538 is_continuation_unreachable = instr_is_unreachable(statement_value);
3594 if (is_continuation_unreachable) {3539 if (is_continuation_unreachable) {
...@@ -6000,22 +5945,6 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -6000,22 +5945,6 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
6000 return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);5945 return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
6001}5946}
60025947
6003static IrInstruction *ir_gen_goto(IrBuilder *irb, Scope *scope, AstNode *node) {
6004 assert(node->type == NodeTypeGoto);
6005
6006 // make a placeholder unreachable statement and a note to come back and
6007 // replace the instruction with a branch instruction
6008 IrGotoItem *goto_item = irb->exec->goto_list.add_one();
6009 goto_item->bb = irb->current_basic_block;
6010 goto_item->instruction_index = irb->current_basic_block->instruction_list.length;
6011 goto_item->source_node = node;
6012 goto_item->scope = scope;
6013
6014 // we don't know if we need to generate defer expressions yet
6015 // we do that later when we find out which label we're jumping to.
6016 return ir_build_unreachable(irb, scope, node);
6017}
6018
6019static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) {5948static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) {
6020 assert(node->type == NodeTypeCompTime);5949 assert(node->type == NodeTypeCompTime);
60215950
...@@ -6033,16 +5962,28 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *...@@ -6033,16 +5962,28 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
60335962
6034 Scope *search_scope = break_scope;5963 Scope *search_scope = break_scope;
6035 ScopeLoop *loop_scope;5964 ScopeLoop *loop_scope;
5965 bool saw_any_loop_scope = false;
6036 for (;;) {5966 for (;;) {
6037 if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {5967 if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
6038 add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));5968 if (saw_any_loop_scope) {
6039 return irb->codegen->invalid_instruction;5969 add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.break_expr.name)));
5970 return irb->codegen->invalid_instruction;
5971 } else {
5972 add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
5973 return irb->codegen->invalid_instruction;
5974 }
6040 } else if (search_scope->id == ScopeIdDeferExpr) {5975 } else if (search_scope->id == ScopeIdDeferExpr) {
6041 add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));5976 add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
6042 return irb->codegen->invalid_instruction;5977 return irb->codegen->invalid_instruction;
6043 } else if (search_scope->id == ScopeIdLoop) {5978 } else if (search_scope->id == ScopeIdLoop) {
6044 loop_scope = (ScopeLoop *)search_scope;5979 ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
6045 break;5980 saw_any_loop_scope = true;
5981 if (node->data.break_expr.name == nullptr ||
5982 (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
5983 {
5984 loop_scope = this_loop_scope;
5985 break;
5986 }
6046 }5987 }
6047 search_scope = search_scope->parent;5988 search_scope = search_scope->parent;
6048 }5989 }
...@@ -6081,16 +6022,28 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast...@@ -6081,16 +6022,28 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
60816022
6082 Scope *search_scope = continue_scope;6023 Scope *search_scope = continue_scope;
6083 ScopeLoop *loop_scope;6024 ScopeLoop *loop_scope;
6025 bool saw_any_loop_scope = false;
6084 for (;;) {6026 for (;;) {
6085 if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {6027 if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
6086 add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));6028 if (saw_any_loop_scope) {
6087 return irb->codegen->invalid_instruction;6029 add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name)));
6030 return irb->codegen->invalid_instruction;
6031 } else {
6032 add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
6033 return irb->codegen->invalid_instruction;
6034 }
6088 } else if (search_scope->id == ScopeIdDeferExpr) {6035 } else if (search_scope->id == ScopeIdDeferExpr) {
6089 add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));6036 add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
6090 return irb->codegen->invalid_instruction;6037 return irb->codegen->invalid_instruction;
6091 } else if (search_scope->id == ScopeIdLoop) {6038 } else if (search_scope->id == ScopeIdLoop) {
6092 loop_scope = (ScopeLoop *)search_scope;6039 ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
6093 break;6040 saw_any_loop_scope = true;
6041 if (node->data.continue_expr.name == nullptr ||
6042 (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
6043 {
6044 loop_scope = this_loop_scope;
6045 break;
6046 }
6094 }6047 }
6095 search_scope = search_scope->parent;6048 search_scope = search_scope->parent;
6096 }6049 }
...@@ -6332,7 +6285,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6332,7 +6285,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6332 case NodeTypeSwitchProng:6285 case NodeTypeSwitchProng:
6333 case NodeTypeSwitchRange:6286 case NodeTypeSwitchRange:
6334 case NodeTypeStructField:6287 case NodeTypeStructField:
6335 case NodeTypeLabel:
6336 case NodeTypeFnDef:6288 case NodeTypeFnDef:
6337 case NodeTypeFnDecl:6289 case NodeTypeFnDecl:
6338 case NodeTypeErrorValueDecl:6290 case NodeTypeErrorValueDecl:
...@@ -6396,8 +6348,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6396,8 +6348,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6396 return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval);6348 return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval);
6397 case NodeTypeSwitchExpr:6349 case NodeTypeSwitchExpr:
6398 return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval);6350 return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval);
6399 case NodeTypeGoto:
6400 return ir_lval_wrap(irb, scope, ir_gen_goto(irb, scope, node), lval);
6401 case NodeTypeCompTime:6351 case NodeTypeCompTime:
6402 return ir_gen_comptime(irb, scope, node, lval);6352 return ir_gen_comptime(irb, scope, node, lval);
6403 case NodeTypeErrorType:6353 case NodeTypeErrorType:
...@@ -6432,70 +6382,6 @@ static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {...@@ -6432,70 +6382,6 @@ static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {
6432 return ir_gen_node_extra(irb, node, scope, LVAL_NONE);6382 return ir_gen_node_extra(irb, node, scope, LVAL_NONE);
6433}6383}
64346384
6435static bool ir_goto_pass2(IrBuilder *irb) {
6436 for (size_t i = 0; i < irb->exec->goto_list.length; i += 1) {
6437 IrGotoItem *goto_item = &irb->exec->goto_list.at(i);
6438 AstNode *source_node = goto_item->source_node;
6439
6440 // Since a goto will always end a basic block, we move the "current instruction"
6441 // index back to over the placeholder unreachable instruction and begin overwriting
6442 irb->current_basic_block = goto_item->bb;
6443 irb->current_basic_block->instruction_list.resize(goto_item->instruction_index);
6444
6445 Buf *label_name = source_node->data.goto_expr.name;
6446
6447 // Search up the scope until we find one of these things:
6448 // * A block scope with the label in it => OK
6449 // * A defer expression scope => error, error, cannot leave defer expression
6450 // * Top level scope => error, didn't find label
6451
6452 LabelTableEntry *label;
6453 Scope *search_scope = goto_item->scope;
6454 for (;;) {
6455 if (search_scope == nullptr) {
6456 add_node_error(irb->codegen, source_node,
6457 buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
6458 return false;
6459 } else if (search_scope->id == ScopeIdBlock) {
6460 ScopeBlock *block_scope = (ScopeBlock *)search_scope;
6461 auto entry = block_scope->label_table.maybe_get(label_name);
6462 if (entry) {
6463 label = entry->value;
6464 break;
6465 }
6466 } else if (search_scope->id == ScopeIdDeferExpr) {
6467 add_node_error(irb->codegen, source_node,
6468 buf_sprintf("cannot goto out of defer expression"));
6469 return false;
6470 }
6471 search_scope = search_scope->parent;
6472 }
6473
6474 label->used = true;
6475
6476 IrInstruction *is_comptime = ir_build_const_bool(irb, goto_item->scope, source_node,
6477 ir_should_inline(irb->exec, goto_item->scope) || source_node->data.goto_expr.is_inline);
6478 if (!ir_gen_defers_for_block(irb, goto_item->scope, label->bb->scope, false)) {
6479 add_node_error(irb->codegen, source_node,
6480 buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
6481 return false;
6482 }
6483 ir_build_br(irb, goto_item->scope, source_node, label->bb, is_comptime);
6484 }
6485
6486 for (size_t i = 0; i < irb->exec->all_labels.length; i += 1) {
6487 LabelTableEntry *label = irb->exec->all_labels.at(i);
6488 if (!label->used) {
6489 add_node_error(irb->codegen, label->decl_node,
6490 buf_sprintf("label '%s' defined but not used",
6491 buf_ptr(label->decl_node->data.label.name)));
6492 return false;
6493 }
6494 }
6495
6496 return true;
6497}
6498
6499static void invalidate_exec(IrExecutable *exec) {6385static void invalidate_exec(IrExecutable *exec) {
6500 if (exec->invalid)6386 if (exec->invalid)
6501 return;6387 return;
...@@ -6532,11 +6418,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -6532,11 +6418,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
6532 ir_mark_gen(ir_build_return(irb, scope, result->source_node, result));6418 ir_mark_gen(ir_build_return(irb, scope, result->source_node, result));
6533 }6419 }
65346420
6535 if (!ir_goto_pass2(irb)) {
6536 invalidate_exec(ir_executable);
6537 return false;
6538 }
6539
6540 return true;6421 return true;
6541}6422}
65426423
src/parser.cpp+106-97
...@@ -632,27 +632,6 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m...@@ -632,27 +632,6 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
632 return node;632 return node;
633}633}
634634
635/*
636GotoExpression = "goto" Symbol
637*/
638static AstNode *ast_parse_goto_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
639 Token *goto_token = &pc->tokens->at(*token_index);
640 if (goto_token->id == TokenIdKeywordGoto) {
641 *token_index += 1;
642 } else if (mandatory) {
643 ast_expect_token(pc, goto_token, TokenIdKeywordGoto);
644 zig_unreachable();
645 } else {
646 return nullptr;
647 }
648
649 AstNode *node = ast_create_node(pc, NodeTypeGoto, goto_token);
650
651 Token *dest_symbol = ast_eat_token(pc, token_index, TokenIdSymbol);
652 node->data.goto_expr.name = token_buf(dest_symbol);
653 return node;
654}
655
656/*635/*
657CompTimeExpression(body) = "comptime" body636CompTimeExpression(body) = "comptime" body
658*/637*/
...@@ -676,8 +655,8 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b...@@ -676,8 +655,8 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
676}655}
677656
678/*657/*
679PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl658PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
680KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"659KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
681*/660*/
682static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {661static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
683 Token *token = &pc->tokens->at(*token_index);662 Token *token = &pc->tokens->at(*token_index);
...@@ -721,6 +700,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo...@@ -721,6 +700,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
721 } else if (token->id == TokenIdKeywordContinue) {700 } else if (token->id == TokenIdKeywordContinue) {
722 AstNode *node = ast_create_node(pc, NodeTypeContinue, token);701 AstNode *node = ast_create_node(pc, NodeTypeContinue, token);
723 *token_index += 1;702 *token_index += 1;
703 Token *maybe_colon_token = &pc->tokens->at(*token_index);
704 if (maybe_colon_token->id == TokenIdColon) {
705 *token_index += 1;
706 Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
707 node->data.continue_expr.name = token_buf(name);
708 }
724 return node;709 return node;
725 } else if (token->id == TokenIdKeywordUndefined) {710 } else if (token->id == TokenIdKeywordUndefined) {
726 AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token);711 AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token);
...@@ -770,10 +755,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo...@@ -770,10 +755,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
770 return node;755 return node;
771 }756 }
772757
773 AstNode *goto_node = ast_parse_goto_expr(pc, token_index, false);
774 if (goto_node)
775 return goto_node;
776
777 AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);758 AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
778 if (grouped_expr_node) {759 if (grouped_expr_node) {
779 return grouped_expr_node;760 return grouped_expr_node;
...@@ -1488,7 +1469,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) {...@@ -1488,7 +1469,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) {
1488}1469}
14891470
1490/*1471/*
1491BreakExpression : "break" option(Expression)1472BreakExpression = "break" option(":" Symbol) option(Expression)
1492*/1473*/
1493static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {1474static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
1494 Token *token = &pc->tokens->at(*token_index);1475 Token *token = &pc->tokens->at(*token_index);
...@@ -1498,8 +1479,15 @@ static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {...@@ -1498,8 +1479,15 @@ static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
1498 } else {1479 } else {
1499 return nullptr;1480 return nullptr;
1500 }1481 }
1501
1502 AstNode *node = ast_create_node(pc, NodeTypeBreak, token);1482 AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
1483
1484 Token *maybe_colon_token = &pc->tokens->at(*token_index);
1485 if (maybe_colon_token->id == TokenIdColon) {
1486 *token_index += 1;
1487 Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
1488 node->data.break_expr.name = token_buf(name);
1489 }
1490
1503 node->data.break_expr.expr = ast_parse_expression(pc, token_index, false);1491 node->data.break_expr.expr = ast_parse_expression(pc, token_index, false);
15041492
1505 return node;1493 return node;
...@@ -1678,35 +1666,53 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, size_t *token_index, bo...@@ -1678,35 +1666,53 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, size_t *token_index, bo
1678}1666}
16791667
1680/*1668/*
1681WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))1669WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
1682*/1670*/
1683static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) {1671static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
1684 Token *first_token = &pc->tokens->at(*token_index);1672 size_t orig_token_index = *token_index;
1685 Token *while_token;
16861673
1687 bool is_inline;1674 Token *name_token = nullptr;
1688 if (first_token->id == TokenIdKeywordInline) {1675 Token *token = &pc->tokens->at(*token_index);
1689 while_token = &pc->tokens->at(*token_index + 1);1676
1690 if (while_token->id == TokenIdKeywordWhile) {1677 if (token->id == TokenIdSymbol) {
1691 is_inline = true;1678 *token_index += 1;
1692 *token_index += 2;1679 Token *colon_token = &pc->tokens->at(*token_index);
1680 if (colon_token->id == TokenIdColon) {
1681 *token_index += 1;
1682 name_token = token;
1683 token = &pc->tokens->at(*token_index);
1693 } else if (mandatory) {1684 } else if (mandatory) {
1694 ast_expect_token(pc, while_token, TokenIdKeywordWhile);1685 ast_expect_token(pc, colon_token, TokenIdColon);
1695 zig_unreachable();1686 zig_unreachable();
1696 } else {1687 } else {
1688 *token_index = orig_token_index;
1697 return nullptr;1689 return nullptr;
1698 }1690 }
1699 } else if (first_token->id == TokenIdKeywordWhile) {1691 }
1700 while_token = first_token;1692
1701 is_inline = false;1693 bool is_inline = false;
1694 if (token->id == TokenIdKeywordInline) {
1695 is_inline = true;
1696 *token_index += 1;
1697 token = &pc->tokens->at(*token_index);
1698 }
1699
1700 Token *while_token;
1701 if (token->id == TokenIdKeywordWhile) {
1702 while_token = token;
1702 *token_index += 1;1703 *token_index += 1;
1703 } else if (mandatory) {1704 } else if (mandatory) {
1704 ast_expect_token(pc, first_token, TokenIdKeywordWhile);1705 ast_expect_token(pc, token, TokenIdKeywordWhile);
1705 zig_unreachable();1706 zig_unreachable();
1706 } else {1707 } else {
1708 *token_index = orig_token_index;
1707 return nullptr;1709 return nullptr;
1708 }1710 }
1711
1709 AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token);1712 AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token);
1713 if (name_token != nullptr) {
1714 node->data.while_expr.name = token_buf(name_token);
1715 }
1710 node->data.while_expr.is_inline = is_inline;1716 node->data.while_expr.is_inline = is_inline;
17111717
1712 ast_eat_token(pc, token_index, TokenIdLParen);1718 ast_eat_token(pc, token_index, TokenIdLParen);
...@@ -1766,36 +1772,53 @@ static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) {...@@ -1766,36 +1772,53 @@ static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) {
1766}1772}
17671773
1768/*1774/*
1769ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))1775ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
1770*/1776*/
1771static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) {1777static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
1772 Token *first_token = &pc->tokens->at(*token_index);1778 size_t orig_token_index = *token_index;
1773 Token *for_token;
17741779
1775 bool is_inline;1780 Token *name_token = nullptr;
1776 if (first_token->id == TokenIdKeywordInline) {1781 Token *token = &pc->tokens->at(*token_index);
1777 is_inline = true;1782
1778 for_token = &pc->tokens->at(*token_index + 1);1783 if (token->id == TokenIdSymbol) {
1779 if (for_token->id == TokenIdKeywordFor) {1784 *token_index += 1;
1780 *token_index += 2;1785 Token *colon_token = &pc->tokens->at(*token_index);
1786 if (colon_token->id == TokenIdColon) {
1787 *token_index += 1;
1788 name_token = token;
1789 token = &pc->tokens->at(*token_index);
1781 } else if (mandatory) {1790 } else if (mandatory) {
1782 ast_expect_token(pc, first_token, TokenIdKeywordFor);1791 ast_expect_token(pc, colon_token, TokenIdColon);
1783 zig_unreachable();1792 zig_unreachable();
1784 } else {1793 } else {
1794 *token_index = orig_token_index;
1785 return nullptr;1795 return nullptr;
1786 }1796 }
1787 } else if (first_token->id == TokenIdKeywordFor) {1797 }
1788 for_token = first_token;1798
1789 is_inline = false;1799 bool is_inline = false;
1800 if (token->id == TokenIdKeywordInline) {
1801 is_inline = true;
1802 *token_index += 1;
1803 token = &pc->tokens->at(*token_index);
1804 }
1805
1806 Token *for_token;
1807 if (token->id == TokenIdKeywordFor) {
1808 for_token = token;
1790 *token_index += 1;1809 *token_index += 1;
1791 } else if (mandatory) {1810 } else if (mandatory) {
1792 ast_expect_token(pc, first_token, TokenIdKeywordFor);1811 ast_expect_token(pc, token, TokenIdKeywordFor);
1793 zig_unreachable();1812 zig_unreachable();
1794 } else {1813 } else {
1814 *token_index = orig_token_index;
1795 return nullptr;1815 return nullptr;
1796 }1816 }
17971817
1798 AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token);1818 AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token);
1819 if (name_token != nullptr) {
1820 node->data.for_expr.name = token_buf(name_token);
1821 }
1799 node->data.for_expr.is_inline = is_inline;1822 node->data.for_expr.is_inline = is_inline;
18001823
1801 ast_eat_token(pc, token_index, TokenIdLParen);1824 ast_eat_token(pc, token_index, TokenIdLParen);
...@@ -2125,32 +2148,6 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool...@@ -2125,32 +2148,6 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool
2125/*2148/*
2126Label: token(Symbol) token(Colon)2149Label: token(Symbol) token(Colon)
2127*/2150*/
2128static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mandatory) {
2129 Token *symbol_token = &pc->tokens->at(*token_index);
2130 if (symbol_token->id != TokenIdSymbol) {
2131 if (mandatory) {
2132 ast_expect_token(pc, symbol_token, TokenIdSymbol);
2133 } else {
2134 return nullptr;
2135 }
2136 }
2137
2138 Token *colon_token = &pc->tokens->at(*token_index + 1);
2139 if (colon_token->id != TokenIdColon) {
2140 if (mandatory) {
2141 ast_expect_token(pc, colon_token, TokenIdColon);
2142 } else {
2143 return nullptr;
2144 }
2145 }
2146
2147 *token_index += 2;
2148
2149 AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token);
2150 node->data.label.name = token_buf(symbol_token);
2151 return node;
2152}
2153
2154static bool statement_terminates_without_semicolon(AstNode *node) {2151static bool statement_terminates_without_semicolon(AstNode *node) {
2155 switch (node->type) {2152 switch (node->type) {
2156 case NodeTypeIfBoolExpr:2153 case NodeTypeIfBoolExpr:
...@@ -2175,7 +2172,6 @@ static bool statement_terminates_without_semicolon(AstNode *node) {...@@ -2175,7 +2172,6 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
2175 return node->data.defer.expr->type == NodeTypeBlock;2172 return node->data.defer.expr->type == NodeTypeBlock;
2176 case NodeTypeSwitchExpr:2173 case NodeTypeSwitchExpr:
2177 case NodeTypeBlock:2174 case NodeTypeBlock:
2178 case NodeTypeLabel:
2179 return true;2175 return true;
2180 default:2176 default:
2181 return false;2177 return false;
...@@ -2183,27 +2179,48 @@ static bool statement_terminates_without_semicolon(AstNode *node) {...@@ -2183,27 +2179,48 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
2183}2179}
21842180
2185/*2181/*
2186Block = "{" many(Statement) option(Expression) "}"2182Block = option(Symbol ":") "{" many(Statement) option(Expression) "}"
2187Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl2183Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl
2188*/2184*/
2189static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) {2185static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) {
2186 size_t orig_token_index = *token_index;
2187
2188 Token *name_token = nullptr;
2190 Token *last_token = &pc->tokens->at(*token_index);2189 Token *last_token = &pc->tokens->at(*token_index);
21912190
2191 if (last_token->id == TokenIdSymbol) {
2192 *token_index += 1;
2193 Token *colon_token = &pc->tokens->at(*token_index);
2194 if (colon_token->id == TokenIdColon) {
2195 *token_index += 1;
2196 name_token = last_token;
2197 last_token = &pc->tokens->at(*token_index);
2198 } else if (mandatory) {
2199 ast_expect_token(pc, colon_token, TokenIdColon);
2200 zig_unreachable();
2201 } else {
2202 *token_index = orig_token_index;
2203 return nullptr;
2204 }
2205 }
2206
2192 if (last_token->id != TokenIdLBrace) {2207 if (last_token->id != TokenIdLBrace) {
2193 if (mandatory) {2208 if (mandatory) {
2194 ast_expect_token(pc, last_token, TokenIdLBrace);2209 ast_expect_token(pc, last_token, TokenIdLBrace);
2195 } else {2210 } else {
2211 *token_index = orig_token_index;
2196 return nullptr;2212 return nullptr;
2197 }2213 }
2198 }2214 }
2199 *token_index += 1;2215 *token_index += 1;
22002216
2201 AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token);2217 AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token);
2218 if (name_token != nullptr) {
2219 node->data.block.name = token_buf(name_token);
2220 }
22022221
2203 for (;;) {2222 for (;;) {
2204 AstNode *statement_node = ast_parse_label(pc, token_index, false);2223 AstNode *statement_node = ast_parse_local_var_decl(pc, token_index);
2205 if (!statement_node)
2206 statement_node = ast_parse_local_var_decl(pc, token_index);
2207 if (!statement_node)2224 if (!statement_node)
2208 statement_node = ast_parse_defer_expr(pc, token_index);2225 statement_node = ast_parse_defer_expr(pc, token_index);
2209 if (!statement_node)2226 if (!statement_node)
...@@ -2225,9 +2242,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand...@@ -2225,9 +2242,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
2225 }2242 }
2226 }2243 }
22272244
2228 node->data.block.last_statement_is_result_expression = statement_node && !(2245 node->data.block.last_statement_is_result_expression = statement_node && statement_node->type != NodeTypeDefer;
2229 statement_node->type == NodeTypeLabel ||
2230 statement_node->type == NodeTypeDefer);
22312246
2232 last_token = &pc->tokens->at(*token_index);2247 last_token = &pc->tokens->at(*token_index);
2233 if (last_token->id == TokenIdRBrace) {2248 if (last_token->id == TokenIdRBrace) {
...@@ -2860,12 +2875,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -2860,12 +2875,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
2860 visit_field(&node->data.switch_range.start, visit, context);2875 visit_field(&node->data.switch_range.start, visit, context);
2861 visit_field(&node->data.switch_range.end, visit, context);2876 visit_field(&node->data.switch_range.end, visit, context);
2862 break;2877 break;
2863 case NodeTypeLabel:
2864 // none
2865 break;
2866 case NodeTypeGoto:
2867 // none
2868 break;
2869 case NodeTypeCompTime:2878 case NodeTypeCompTime:
2870 visit_field(&node->data.comptime_expr.expr, visit, context);2879 visit_field(&node->data.comptime_expr.expr, visit, context);
2871 break;2880 break;
src/translate_c.cpp+7-177
...@@ -104,10 +104,8 @@ static TransScopeRoot *trans_scope_root_create(Context *c);...@@ -104,10 +104,8 @@ static TransScopeRoot *trans_scope_root_create(Context *c);
104static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope);104static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope);
105static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);105static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);
106static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);106static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);
107static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
108107
109static TransScopeBlock *trans_scope_block_find(TransScope *scope);108static TransScopeBlock *trans_scope_block_find(TransScope *scope);
110static TransScopeSwitch *trans_scope_switch_find(TransScope *scope);
111109
112static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);110static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
113static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);111static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
...@@ -265,18 +263,6 @@ static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_vol...@@ -265,18 +263,6 @@ static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_vol
265 return node;263 return node;
266}264}
267265
268static AstNode *trans_create_node_goto(Context *c, Buf *label_name) {
269 AstNode *goto_node = trans_create_node(c, NodeTypeGoto);
270 goto_node->data.goto_expr.name = label_name;
271 return goto_node;
272}
273
274static AstNode *trans_create_node_label(Context *c, Buf *label_name) {
275 AstNode *label_node = trans_create_node(c, NodeTypeLabel);
276 label_node->data.label.name = label_name;
277 return label_node;
278}
279
280static AstNode *trans_create_node_bool(Context *c, bool value) {266static AstNode *trans_create_node_bool(Context *c, bool value) {
281 AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral);267 AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral);
282 bool_node->data.bool_literal.value = value;268 bool_node->data.bool_literal.value = value;
...@@ -2379,145 +2365,6 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt...@@ -2379,145 +2365,6 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
2379 return while_scope->node;2365 return while_scope->node;
2380}2366}
23812367
2382static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) {
2383 TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope);
2384
2385 TransScopeSwitch *switch_scope;
2386
2387 const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
2388 if (var_decl_stmt == nullptr) {
2389 switch_scope = trans_scope_switch_create(c, &block_scope->base);
2390 } else {
2391 AstNode *vars_node;
2392 TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node);
2393 if (var_scope == nullptr)
2394 return nullptr;
2395 if (vars_node != nullptr)
2396 block_scope->node->data.block.statements.append(vars_node);
2397 switch_scope = trans_scope_switch_create(c, var_scope);
2398 }
2399 block_scope->node->data.block.statements.append(switch_scope->switch_node);
2400
2401 // TODO avoid name collisions
2402 Buf *end_label_name = buf_create_from_str("end");
2403 switch_scope->end_label_name = end_label_name;
2404
2405 const Expr *cond_expr = stmt->getCond();
2406 assert(cond_expr != nullptr);
2407
2408 AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue);
2409 if (expr_node == nullptr)
2410 return nullptr;
2411 switch_scope->switch_node->data.switch_expr.expr = expr_node;
2412
2413 AstNode *body_node;
2414 const Stmt *body_stmt = stmt->getBody();
2415 if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) {
2416 if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt,
2417 block_scope->node, nullptr))
2418 {
2419 return nullptr;
2420 }
2421 } else {
2422 TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node);
2423 if (body_scope == nullptr)
2424 return nullptr;
2425 if (body_node != nullptr)
2426 block_scope->node->data.block.statements.append(body_node);
2427 }
2428
2429 if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) {
2430 AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
2431 prong_node->data.switch_prong.expr = trans_create_node_goto(c, end_label_name);
2432 switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
2433 }
2434
2435 // This is necessary if the last switch case "falls through" the end of the switch block
2436 block_scope->node->data.block.statements.append(trans_create_node_goto(c, end_label_name));
2437
2438 block_scope->node->data.block.statements.append(trans_create_node_label(c, end_label_name));
2439
2440 return block_scope->node;
2441}
2442
2443static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node,
2444 TransScope **out_scope)
2445{
2446 *out_node = nullptr;
2447
2448 if (stmt->getRHS() != nullptr) {
2449 emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension");
2450 return ErrorUnexpected;
2451 }
2452
2453 TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
2454 assert(switch_scope != nullptr);
2455
2456 Buf *label_name = buf_sprintf("case_%" PRIu32, switch_scope->case_index);
2457 switch_scope->case_index += 1;
2458
2459 {
2460 // Add the prong
2461 AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
2462 AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue);
2463 if (item_node == nullptr)
2464 return ErrorUnexpected;
2465 prong_node->data.switch_prong.items.append(item_node);
2466
2467 prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
2468
2469 switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
2470 }
2471
2472 TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
2473 scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
2474
2475 AstNode *sub_stmt_node;
2476 TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
2477 if (new_scope == nullptr)
2478 return ErrorUnexpected;
2479 if (sub_stmt_node != nullptr)
2480 scope_block->node->data.block.statements.append(sub_stmt_node);
2481
2482 *out_scope = new_scope;
2483 return ErrorNone;
2484}
2485
2486static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node,
2487 TransScope **out_scope)
2488{
2489 *out_node = nullptr;
2490
2491 TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
2492 assert(switch_scope != nullptr);
2493
2494 Buf *label_name = buf_sprintf("default");
2495
2496 {
2497 // Add the prong
2498 AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
2499
2500 prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
2501
2502 switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
2503 switch_scope->found_default = true;
2504 }
2505
2506 TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
2507 scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
2508
2509
2510 AstNode *sub_stmt_node;
2511 TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
2512 if (new_scope == nullptr)
2513 return ErrorUnexpected;
2514 if (sub_stmt_node != nullptr)
2515 scope_block->node->data.block.statements.append(sub_stmt_node);
2516
2517 *out_scope = new_scope;
2518 return ErrorNone;
2519}
2520
2521static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {2368static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {
2522 AstNode *loop_block_node;2369 AstNode *loop_block_node;
2523 TransScopeWhile *while_scope;2370 TransScopeWhile *while_scope;
...@@ -2595,8 +2442,7 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt...@@ -2595,8 +2442,7 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt
2595 if (cur_scope->id == TransScopeIdWhile) {2442 if (cur_scope->id == TransScopeIdWhile) {
2596 return trans_create_node(c, NodeTypeBreak);2443 return trans_create_node(c, NodeTypeBreak);
2597 } else if (cur_scope->id == TransScopeIdSwitch) {2444 } else if (cur_scope->id == TransScopeIdSwitch) {
2598 TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope;2445 zig_panic("TODO");
2599 return trans_create_node_goto(c, switch_scope->end_label_name);
2600 }2446 }
2601 cur_scope = cur_scope->parent;2447 cur_scope = cur_scope->parent;
2602 }2448 }
...@@ -2696,12 +2542,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,...@@ -2696,12 +2542,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
2696 return wrap_stmt(out_node, out_child_scope, scope,2542 return wrap_stmt(out_node, out_child_scope, scope,
2697 trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue));2543 trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue));
2698 case Stmt::SwitchStmtClass:2544 case Stmt::SwitchStmtClass:
2699 return wrap_stmt(out_node, out_child_scope, scope,2545 emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
2700 trans_switch_stmt(c, scope, (const SwitchStmt *)stmt));2546 return ErrorUnexpected;
2701 case Stmt::CaseStmtClass:2547 case Stmt::CaseStmtClass:
2702 return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope);2548 emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass");
2549 return ErrorUnexpected;
2703 case Stmt::DefaultStmtClass:2550 case Stmt::DefaultStmtClass:
2704 return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope);2551 emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass");
2552 return ErrorUnexpected;
2705 case Stmt::NoStmtClass:2553 case Stmt::NoStmtClass:
2706 emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");2554 emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
2707 return ErrorUnexpected;2555 return ErrorUnexpected;
...@@ -3871,14 +3719,6 @@ static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scop...@@ -3871,14 +3719,6 @@ static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scop
3871 return result;3719 return result;
3872}3720}
38733721
3874static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
3875 TransScopeSwitch *result = allocate<TransScopeSwitch>(1);
3876 result->base.id = TransScopeIdSwitch;
3877 result->base.parent = parent_scope;
3878 result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
3879 return result;
3880}
3881
3882static TransScopeBlock *trans_scope_block_find(TransScope *scope) {3722static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
3883 while (scope != nullptr) {3723 while (scope != nullptr) {
3884 if (scope->id == TransScopeIdBlock) {3724 if (scope->id == TransScopeIdBlock) {
...@@ -3889,16 +3729,6 @@ static TransScopeBlock *trans_scope_block_find(TransScope *scope) {...@@ -3889,16 +3729,6 @@ static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
3889 return nullptr;3729 return nullptr;
3890}3730}
38913731
3892static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) {
3893 while (scope != nullptr) {
3894 if (scope->id == TransScopeIdSwitch) {
3895 return (TransScopeSwitch *)scope;
3896 }
3897 scope = scope->parent;
3898 }
3899 return nullptr;
3900}
3901
3902static void render_aliases(Context *c) {3732static void render_aliases(Context *c) {
3903 for (size_t i = 0; i < c->aliases.length; i += 1) {3733 for (size_t i = 0; i < c->aliases.length; i += 1) {
3904 Alias *alias = &c->aliases.at(i);3734 Alias *alias = &c->aliases.at(i);
std/elf.zig+2-4
...@@ -243,7 +243,7 @@ pub const Elf = struct {...@@ -243,7 +243,7 @@ pub const Elf = struct {
243 var file_stream = io.FileInStream.init(elf.in_file);243 var file_stream = io.FileInStream.init(elf.in_file);
244 const in = &file_stream.stream;244 const in = &file_stream.stream;
245245
246 for (elf.section_headers) |*elf_section| {246 section_loop: for (elf.section_headers) |*elf_section| {
247 if (elf_section.sh_type == SHT_NULL) continue;247 if (elf_section.sh_type == SHT_NULL) continue;
248248
249 const name_offset = elf.string_section.offset + elf_section.name;249 const name_offset = elf.string_section.offset + elf_section.name;
...@@ -251,15 +251,13 @@ pub const Elf = struct {...@@ -251,15 +251,13 @@ pub const Elf = struct {
251251
252 for (name) |expected_c| {252 for (name) |expected_c| {
253 const target_c = %return in.readByte();253 const target_c = %return in.readByte();
254 if (target_c == 0 or expected_c != target_c) goto next_section;254 if (target_c == 0 or expected_c != target_c) continue :section_loop;
255 }255 }
256256
257 {257 {
258 const null_byte = %return in.readByte();258 const null_byte = %return in.readByte();
259 if (null_byte == 0) return elf_section;259 if (null_byte == 0) return elf_section;
260 }260 }
261
262 next_section:
263 }261 }
264262
265 return null;263 return null;
std/os/index.zig+74-72
...@@ -902,40 +902,41 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void {...@@ -902,40 +902,41 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void {
902/// this function recursively removes its entries and then tries again.902/// this function recursively removes its entries and then tries again.
903// TODO non-recursive implementation903// TODO non-recursive implementation
904pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void {904pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void {
905start_over:905 start_over: while (true) {
906 // First, try deleting the item as a file. This way we don't follow sym links.906 // First, try deleting the item as a file. This way we don't follow sym links.
907 if (deleteFile(allocator, full_path)) {907 if (deleteFile(allocator, full_path)) {
908 return;
909 } else |err| {
910 if (err == error.FileNotFound)
911 return;908 return;
912 if (err != error.IsDir)909 } else |err| {
913 return err;
914 }
915 {
916 var dir = Dir.open(allocator, full_path) %% |err| {
917 if (err == error.FileNotFound)910 if (err == error.FileNotFound)
918 return;911 return;
919 if (err == error.NotDir)912 if (err != error.IsDir)
920 goto start_over;913 return err;
921 return err;914 }
922 };915 {
923 defer dir.close();916 var dir = Dir.open(allocator, full_path) %% |err| {
917 if (err == error.FileNotFound)
918 return;
919 if (err == error.NotDir)
920 continue :start_over;
921 return err;
922 };
923 defer dir.close();
924924
925 var full_entry_buf = ArrayList(u8).init(allocator);925 var full_entry_buf = ArrayList(u8).init(allocator);
926 defer full_entry_buf.deinit();926 defer full_entry_buf.deinit();
927927
928 while (%return dir.next()) |entry| {928 while (%return dir.next()) |entry| {
929 %return full_entry_buf.resize(full_path.len + entry.name.len + 1);929 %return full_entry_buf.resize(full_path.len + entry.name.len + 1);
930 const full_entry_path = full_entry_buf.toSlice();930 const full_entry_path = full_entry_buf.toSlice();
931 mem.copy(u8, full_entry_path, full_path);931 mem.copy(u8, full_entry_path, full_path);
932 full_entry_path[full_path.len] = '/';932 full_entry_path[full_path.len] = '/';
933 mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name);933 mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name);
934934
935 %return deleteTree(allocator, full_entry_path);935 %return deleteTree(allocator, full_entry_path);
936 }
936 }937 }
938 return deleteDir(allocator, full_path);
937 }939 }
938 return deleteDir(allocator, full_path);
939}940}
940941
941pub const Dir = struct {942pub const Dir = struct {
...@@ -988,58 +989,59 @@ pub const Dir = struct {...@@ -988,58 +989,59 @@ pub const Dir = struct {
988 /// Memory such as file names referenced in this returned entry becomes invalid989 /// Memory such as file names referenced in this returned entry becomes invalid
989 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.990 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.
990 pub fn next(self: &Dir) -> %?Entry {991 pub fn next(self: &Dir) -> %?Entry {
991 start_over:992 start_over: while (true) {
992 if (self.index >= self.end_index) {993 if (self.index >= self.end_index) {
993 if (self.buf.len == 0) {994 if (self.buf.len == 0) {
994 self.buf = %return self.allocator.alloc(u8, page_size);995 self.buf = %return self.allocator.alloc(u8, page_size);
995 }996 }
996997
997 while (true) {998 while (true) {
998 const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);999 const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
999 const err = linux.getErrno(result);1000 const err = linux.getErrno(result);
1000 if (err > 0) {1001 if (err > 0) {
1001 switch (err) {1002 switch (err) {
1002 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1003 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
1003 posix.EINVAL => {1004 posix.EINVAL => {
1004 self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);1005 self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
1005 continue;1006 continue;
1006 },1007 },
1007 else => return unexpectedErrorPosix(err),1008 else => return unexpectedErrorPosix(err),
1008 };1009 };
1010 }
1011 if (result == 0)
1012 return null;
1013 self.index = 0;
1014 self.end_index = result;
1015 break;
1009 }1016 }
1010 if (result == 0)
1011 return null;
1012 self.index = 0;
1013 self.end_index = result;
1014 break;
1015 }1017 }
1016 }1018 const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
1017 const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);1019 const next_index = self.index + linux_entry.d_reclen;
1018 const next_index = self.index + linux_entry.d_reclen;1020 self.index = next_index;
1019 self.index = next_index;
10201021
1021 const name = cstr.toSlice(&linux_entry.d_name);1022 const name = cstr.toSlice(&linux_entry.d_name);
10221023
1023 // skip . and .. entries1024 // skip . and .. entries
1024 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {1025 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
1025 goto start_over;1026 continue :start_over;
1026 }1027 }
10271028
1028 const type_char = self.buf[next_index - 1];1029 const type_char = self.buf[next_index - 1];
1029 const entry_kind = switch (type_char) {1030 const entry_kind = switch (type_char) {
1030 posix.DT_BLK => Entry.Kind.BlockDevice,1031 posix.DT_BLK => Entry.Kind.BlockDevice,
1031 posix.DT_CHR => Entry.Kind.CharacterDevice,1032 posix.DT_CHR => Entry.Kind.CharacterDevice,
1032 posix.DT_DIR => Entry.Kind.Directory,1033 posix.DT_DIR => Entry.Kind.Directory,
1033 posix.DT_FIFO => Entry.Kind.NamedPipe,1034 posix.DT_FIFO => Entry.Kind.NamedPipe,
1034 posix.DT_LNK => Entry.Kind.SymLink,1035 posix.DT_LNK => Entry.Kind.SymLink,
1035 posix.DT_REG => Entry.Kind.File,1036 posix.DT_REG => Entry.Kind.File,
1036 posix.DT_SOCK => Entry.Kind.UnixDomainSocket,1037 posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
1037 else => Entry.Kind.Unknown,1038 else => Entry.Kind.Unknown,
1038 };1039 };
1039 return Entry {1040 return Entry {
1040 .name = name,1041 .name = name,
1041 .kind = entry_kind,1042 .kind = entry_kind,
1042 };1043 };
1044 }
1043 }1045 }
1044};1046};
10451047
test/behavior.zig-1
...@@ -20,7 +20,6 @@ comptime {...@@ -20,7 +20,6 @@ comptime {
20 _ = @import("cases/fn.zig");20 _ = @import("cases/fn.zig");
21 _ = @import("cases/for.zig");21 _ = @import("cases/for.zig");
22 _ = @import("cases/generics.zig");22 _ = @import("cases/generics.zig");
23 _ = @import("cases/goto.zig");
24 _ = @import("cases/if.zig");23 _ = @import("cases/if.zig");
25 _ = @import("cases/import.zig");24 _ = @import("cases/import.zig");
26 _ = @import("cases/incomplete_struct_param_tld.zig");25 _ = @import("cases/incomplete_struct_param_tld.zig");
test/cases/for.zig+34
...@@ -55,3 +55,37 @@ test "basic for loop" {...@@ -55,3 +55,37 @@ test "basic for loop" {
5555
56 assert(mem.eql(u8, buffer[0..buf_index], expected_result));56 assert(mem.eql(u8, buffer[0..buf_index], expected_result));
57}57}
58
59test "break from outer for loop" {
60 testBreakOuter();
61 comptime testBreakOuter();
62}
63
64fn testBreakOuter() {
65 var array = "aoeu";
66 var count: usize = 0;
67 outer: for (array) |_| {
68 for (array) |_2| { // TODO shouldn't get error for redeclaring "_"
69 count += 1;
70 break :outer;
71 }
72 }
73 assert(count == 1);
74}
75
76test "continue outer for loop" {
77 testContinueOuter();
78 comptime testContinueOuter();
79}
80
81fn testContinueOuter() {
82 var array = "aoeu";
83 var counter: usize = 0;
84 outer: for (array) |_| {
85 for (array) |_2| { // TODO shouldn't get error for redeclaring "_"
86 counter += 1;
87 continue :outer;
88 }
89 }
90 assert(counter == array.len);
91}
test/cases/goto.zig deleted-37
...@@ -1,37 +0,0 @@
1const assert = @import("std").debug.assert;
2
3test "goto and labels" {
4 gotoLoop();
5 assert(goto_counter == 10);
6}
7fn gotoLoop() {
8 var i: i32 = 0;
9 goto cond;
10loop:
11 i += 1;
12cond:
13 if (!(i < 10)) goto end;
14 goto_counter += 1;
15 goto loop;
16end:
17}
18var goto_counter: i32 = 0;
19
20
21
22test "goto leave defer scope" {
23 testGotoLeaveDeferScope(true);
24}
25fn testGotoLeaveDeferScope(b: bool) {
26 var it_worked = false;
27
28 goto entry;
29exit:
30 if (it_worked) {
31 return;
32 }
33 unreachable;
34entry:
35 defer it_worked = true;
36 if (b) goto exit;
37}
test/cases/while.zig+27
...@@ -188,6 +188,33 @@ test "while on bool with else result follow break prong" {...@@ -188,6 +188,33 @@ test "while on bool with else result follow break prong" {
188 assert(result == 10);188 assert(result == 10);
189}189}
190190
191test "break from outer while loop" {
192 testBreakOuter();
193 comptime testBreakOuter();
194}
195
196fn testBreakOuter() {
197 outer: while (true) {
198 while (true) {
199 break :outer;
200 }
201 }
202}
203
204test "continue outer while loop" {
205 testContinueOuter();
206 comptime testContinueOuter();
207}
208
209fn testContinueOuter() {
210 var i: usize = 0;
211 outer: while (i < 10) : (i += 1) {
212 while (true) {
213 continue :outer;
214 }
215 }
216}
217
191fn returnNull() -> ?i32 { null }218fn returnNull() -> ?i32 { null }
192fn returnMaybe(x: i32) -> ?i32 { x }219fn returnMaybe(x: i32) -> ?i32 { x }
193error YouWantedAnError;220error YouWantedAnError;
test/compile_errors.zig+21-30
...@@ -1,6 +1,27 @@...@@ -1,6 +1,27 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompileErrorContext) {3pub fn addCases(cases: &tests.CompileErrorContext) {
4 cases.add("labeled break not found",
5 \\export fn entry() {
6 \\ blah: while (true) {
7 \\ while (true) {
8 \\ break :outer;
9 \\ }
10 \\ }
11 \\}
12 , ".tmp_source.zig:4:13: error: labeled loop not found: 'outer'");
13
14 cases.add("labeled continue not found",
15 \\export fn entry() {
16 \\ var i: usize = 0;
17 \\ blah: while (i < 10) : (i += 1) {
18 \\ while (true) {
19 \\ continue :outer;
20 \\ }
21 \\ }
22 \\}
23 , ".tmp_source.zig:5:13: error: labeled loop not found: 'outer'");
24
4 cases.add("attempt to use 0 bit type in extern fn",25 cases.add("attempt to use 0 bit type in extern fn",
5 \\extern fn foo(ptr: extern fn(&void));26 \\extern fn foo(ptr: extern fn(&void));
6 \\27 \\
...@@ -833,26 +854,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -833,26 +854,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
833 \\export fn entry() -> usize { @sizeOf(@typeOf(test1)) }854 \\export fn entry() -> usize { @sizeOf(@typeOf(test1)) }
834 , ".tmp_source.zig:3:16: error: unable to evaluate constant expression");855 , ".tmp_source.zig:3:16: error: unable to evaluate constant expression");
835856
836 cases.add("goto jumping into block",
837 \\export fn f() {
838 \\ {
839 \\a_label:
840 \\ }
841 \\ goto a_label;
842 \\}
843 , ".tmp_source.zig:5:5: error: no label in scope named 'a_label'");
844
845 cases.add("goto jumping past a defer",
846 \\fn f(b: bool) {
847 \\ if (b) goto label;
848 \\ defer derp();
849 \\label:
850 \\}
851 \\fn derp(){}
852 \\
853 \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
854 , ".tmp_source.zig:2:12: error: no label in scope named 'label'");
855
856 cases.add("assign null to non-nullable pointer",857 cases.add("assign null to non-nullable pointer",
857 \\const a: &u8 = null;858 \\const a: &u8 = null;
858 \\859 \\
...@@ -1854,16 +1855,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -1854,16 +1855,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
1854 ,1855 ,
1855 ".tmp_source.zig:4:13: error: cannot continue out of defer expression");1856 ".tmp_source.zig:4:13: error: cannot continue out of defer expression");
18561857
1857 cases.add("cannot goto out of defer expression",
1858 \\export fn foo() {
1859 \\ defer {
1860 \\ goto label;
1861 \\ };
1862 \\label:
1863 \\}
1864 ,
1865 ".tmp_source.zig:3:9: error: cannot goto out of defer expression");
1866
1867 cases.add("calling a var args function only known at runtime",1858 cases.add("calling a var args function only known at runtime",
1868 \\var foos = []fn(...) { foo1, foo2 };1859 \\var foos = []fn(...) { foo1, foo2 };
1869 \\1860 \\
test/translate_c.zig-80
...@@ -1005,48 +1005,6 @@ pub fn addCases(cases: &tests.TranslateCContext) {...@@ -1005,48 +1005,6 @@ pub fn addCases(cases: &tests.TranslateCContext) {
1005 \\}1005 \\}
1006 );1006 );
10071007
1008 cases.add("switch statement",
1009 \\int foo(int x) {
1010 \\ switch (x) {
1011 \\ case 1:
1012 \\ x += 1;
1013 \\ case 2:
1014 \\ break;
1015 \\ case 3:
1016 \\ case 4:
1017 \\ return x + 1;
1018 \\ default:
1019 \\ return 10;
1020 \\ }
1021 \\ return x + 13;
1022 \\}
1023 ,
1024 \\fn foo(_arg_x: c_int) -> c_int {
1025 \\ var x = _arg_x;
1026 \\ {
1027 \\ switch (x) {
1028 \\ 1 => goto case_0,
1029 \\ 2 => goto case_1,
1030 \\ 3 => goto case_2,
1031 \\ 4 => goto case_3,
1032 \\ else => goto default,
1033 \\ };
1034 \\ case_0:
1035 \\ x += 1;
1036 \\ case_1:
1037 \\ goto end;
1038 \\ case_2:
1039 \\ case_3:
1040 \\ return x + 1;
1041 \\ default:
1042 \\ return 10;
1043 \\ goto end;
1044 \\ end:
1045 \\ };
1046 \\ return x + 13;
1047 \\}
1048 );
1049
1050 cases.add("macros with field targets",1008 cases.add("macros with field targets",
1051 \\typedef unsigned int GLbitfield;1009 \\typedef unsigned int GLbitfield;
1052 \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);1010 \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
...@@ -1085,44 +1043,6 @@ pub fn addCases(cases: &tests.TranslateCContext) {...@@ -1085,44 +1043,6 @@ pub fn addCases(cases: &tests.TranslateCContext) {
1085 \\pub const OpenGLProcs = union_OpenGLProcs;1043 \\pub const OpenGLProcs = union_OpenGLProcs;
1086 );1044 );
10871045
1088 cases.add("switch statement with no default",
1089 \\int foo(int x) {
1090 \\ switch (x) {
1091 \\ case 1:
1092 \\ x += 1;
1093 \\ case 2:
1094 \\ break;
1095 \\ case 3:
1096 \\ case 4:
1097 \\ return x + 1;
1098 \\ }
1099 \\ return x + 13;
1100 \\}
1101 ,
1102 \\fn foo(_arg_x: c_int) -> c_int {
1103 \\ var x = _arg_x;
1104 \\ {
1105 \\ switch (x) {
1106 \\ 1 => goto case_0,
1107 \\ 2 => goto case_1,
1108 \\ 3 => goto case_2,
1109 \\ 4 => goto case_3,
1110 \\ else => goto end,
1111 \\ };
1112 \\ case_0:
1113 \\ x += 1;
1114 \\ case_1:
1115 \\ goto end;
1116 \\ case_2:
1117 \\ case_3:
1118 \\ return x + 1;
1119 \\ goto end;
1120 \\ end:
1121 \\ };
1122 \\ return x + 13;
1123 \\}
1124 );
1125
1126 cases.add("variable name shadowing",1046 cases.add("variable name shadowing",
1127 \\int foo(void) {1047 \\int foo(void) {
1128 \\ int x = 1;1048 \\ int x = 1;