| author | |
| committer | |
| log | 8bc523219c66427951e5339550502871547f2138 |
| tree | 207f1b9335a6bdd2d25a96c206430daf91087e24 |
| parent | d686113bd2b2e2207137de6ef81e515bc4a3aa07 |
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, ",") ")" |
| 5847 | 5847 | ||
| 5848 | ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...") | 5848 | ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...") |
| 5849 | 5849 | ||
| 5850 | Block = "{" many(Statement) option(Expression) "}" | 5850 | Block = option(Symbol ":") "{" many(Statement) option(Expression) "}" |
| 5851 | 5851 | ||
| 5852 | Statement = Label | LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | 5852 | Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" |
| 5853 | |||
| 5854 | Label = Symbol ":" | ||
| 5855 | 5853 | ||
| 5856 | TypeExpr = PrefixOpExpression | "var" | 5854 | TypeExpr = PrefixOpExpression | "var" |
| 5857 | 5855 | ||
| ... | @@ -5891,13 +5889,13 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Sy | ... | @@ -5891,13 +5889,13 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Sy |
| 5891 | 5889 | ||
| 5892 | SwitchItem = Expression | (Expression "..." Expression) | 5890 | SwitchItem = Expression | (Expression "..." Expression) |
| 5893 | 5891 | ||
| 5894 | ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) | 5892 | ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) |
| 5895 | 5893 | ||
| 5896 | BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression | 5894 | BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression |
| 5897 | 5895 | ||
| 5898 | ReturnExpression = option("%") "return" option(Expression) | 5896 | ReturnExpression = option("%") "return" option(Expression) |
| 5899 | 5897 | ||
| 5900 | BreakExpression = "break" option(Expression) | 5898 | BreakExpression = "break" option(":" Symbol) option(Expression) |
| 5901 | 5899 | ||
| 5902 | Defer(body) = option("%") "defer" body | 5900 | Defer(body) = option("%") "defer" body |
| 5903 | 5901 | ||
| ... | @@ -5907,7 +5905,7 @@ TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") | ... | @@ -5907,7 +5905,7 @@ TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") |
| 5907 | 5905 | ||
| 5908 | TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body)) | 5906 | TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body)) |
| 5909 | 5907 | ||
| 5910 | WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) | 5908 | WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) |
| 5911 | 5909 | ||
| 5912 | BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression | 5910 | BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression |
| 5913 | 5911 | ||
| ... | @@ -5955,15 +5953,13 @@ StructLiteralField = "." Symbol "=" Expression | ... | @@ -5955,15 +5953,13 @@ StructLiteralField = "." Symbol "=" Expression |
| 5955 | 5953 | ||
| 5956 | PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" | 5954 | PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" |
| 5957 | 5955 | ||
| 5958 | PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | 5956 | PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol)) |
| 5959 | 5957 | ||
| 5960 | ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr | 5958 | ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr |
| 5961 | 5959 | ||
| 5962 | GotoExpression = "goto" Symbol | ||
| 5963 | |||
| 5964 | GroupedExpression = "(" Expression ")" | 5960 | GroupedExpression = "(" Expression ")" |
| 5965 | 5961 | ||
| 5966 | KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable" | 5962 | KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" |
| 5967 | 5963 | ||
| 5968 | ContainerDecl = option("extern" | "packed") | 5964 | ContainerDecl = 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 | }; |
| 453 | 451 | ||
| 454 | struct AstNodeBlock { | 452 | struct 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 | }; |
| 663 | 662 | ||
| 664 | struct AstNodeWhileExpr { | 663 | struct 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 | }; |
| 674 | 674 | ||
| 675 | struct AstNodeForExpr { | 675 | struct AstNodeForExpr { |
| 676 | Buf *name; | ||
| 676 | AstNode *array_expr; | 677 | AstNode *array_expr; |
| 677 | AstNode *elem_node; // always a symbol | 678 | AstNode *elem_node; // always a symbol |
| 678 | AstNode *index_node; // always a symbol, might be null | 679 | 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 | }; |
| 706 | 707 | ||
| 707 | struct AstNodeGoto { | ||
| 708 | Buf *name; | ||
| 709 | bool is_inline; | ||
| 710 | }; | ||
| 711 | |||
| 712 | struct AstNodeCompTime { | 708 | struct AstNodeCompTime { |
| 713 | AstNode *expr; | 709 | AstNode *expr; |
| 714 | }; | 710 | }; |
| ... | @@ -836,11 +832,14 @@ struct AstNodeBoolLiteral { | ... | @@ -836,11 +832,14 @@ struct AstNodeBoolLiteral { |
| 836 | }; | 832 | }; |
| 837 | 833 | ||
| 838 | struct AstNodeBreakExpr { | 834 | struct AstNodeBreakExpr { |
| 835 | Buf *name; | ||
| 839 | AstNode *expr; // may be null | 836 | AstNode *expr; // may be null |
| 840 | }; | 837 | }; |
| 841 | 838 | ||
| 842 | struct AstNodeContinueExpr { | 839 | struct AstNodeContinueExpr { |
| 840 | Buf *name; | ||
| 843 | }; | 841 | }; |
| 842 | |||
| 844 | struct AstNodeUnreachableExpr { | 843 | struct AstNodeUnreachableExpr { |
| 845 | }; | 844 | }; |
| 846 | 845 | ||
| ... | @@ -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 { |
| 1741 | struct ScopeLoop { | 1739 | struct ScopeLoop { |
| 1742 | Scope base; | 1740 | Scope base; |
| 1743 | 1741 | ||
| 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 | } |
| 145 | 145 | ||
| 146 | ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) { | 146 | ScopeLoop *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 | } |
| 152 | 158 | ||
| ... | @@ -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 | } |
| 3513 | 3513 | ||
| 3514 | static 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 | |||
| 3528 | static 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 | |||
| 3537 | static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) { | 3514 | static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) { |
| 3538 | assert(block_node->type == NodeTypeBlock); | 3515 | assert(block_node->type == NodeTypeBlock); |
| 3539 | 3516 | ||
| ... | @@ -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); |
| 3559 | 3536 | ||
| 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 | } |
| 6002 | 5947 | ||
| 6003 | static 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 | |||
| 6019 | static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) { | 5948 | static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) { |
| 6020 | assert(node->type == NodeTypeCompTime); | 5949 | assert(node->type == NodeTypeCompTime); |
| 6021 | 5950 | ||
| ... | @@ -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 * |
| 6033 | 5962 | ||
| 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 |
| 6081 | 6022 | ||
| 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 | } |
| 6434 | 6384 | ||
| 6435 | static 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 | |||
| 6499 | static void invalidate_exec(IrExecutable *exec) { | 6385 | static 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 | } |
| 6534 | 6420 | ||
| 6535 | if (!ir_goto_pass2(irb)) { | ||
| 6536 | invalidate_exec(ir_executable); | ||
| 6537 | return false; | ||
| 6538 | } | ||
| 6539 | |||
| 6540 | return true; | 6421 | return true; |
| 6541 | } | 6422 | } |
| 6542 | 6423 |
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 | } |
| 634 | 634 | ||
| 635 | /* | ||
| 636 | GotoExpression = "goto" Symbol | ||
| 637 | */ | ||
| 638 | static 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 | /* |
| 657 | CompTimeExpression(body) = "comptime" body | 636 | CompTimeExpression(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 | } |
| 677 | 656 | ||
| 678 | /* | 657 | /* |
| 679 | PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | 658 | PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol)) |
| 680 | KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable" | 659 | KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" |
| 681 | */ | 660 | */ |
| 682 | static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | 661 | static 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 | } |
| 772 | 757 | ||
| 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 | } |
| 1489 | 1470 | ||
| 1490 | /* | 1471 | /* |
| 1491 | BreakExpression : "break" option(Expression) | 1472 | BreakExpression = "break" option(":" Symbol) option(Expression) |
| 1492 | */ | 1473 | */ |
| 1493 | static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) { | 1474 | static 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); |
| 1504 | 1492 | ||
| 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 | } |
| 1679 | 1667 | ||
| 1680 | /* | 1668 | /* |
| 1681 | WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) | 1669 | WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) |
| 1682 | */ | 1670 | */ |
| 1683 | static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | 1671 | static 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; | ||
| 1686 | 1673 | ||
| 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; |
| 1711 | 1717 | ||
| 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 | } |
| 1767 | 1773 | ||
| 1768 | /* | 1774 | /* |
| 1769 | ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) | 1775 | ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) |
| 1770 | */ | 1776 | */ |
| 1771 | static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | 1777 | static 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; | ||
| 1774 | 1779 | ||
| 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 | } |
| 1797 | 1817 | ||
| 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; |
| 1800 | 1823 | ||
| 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 | /* |
| 2126 | Label: token(Symbol) token(Colon) | 2149 | Label: token(Symbol) token(Colon) |
| 2127 | */ | 2150 | */ |
| 2128 | static 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 | |||
| 2154 | static bool statement_terminates_without_semicolon(AstNode *node) { | 2151 | static 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 | } |
| 2184 | 2180 | ||
| 2185 | /* | 2181 | /* |
| 2186 | Block = "{" many(Statement) option(Expression) "}" | 2182 | Block = option(Symbol ":") "{" many(Statement) option(Expression) "}" |
| 2187 | Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl | 2183 | Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl |
| 2188 | */ | 2184 | */ |
| 2189 | static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) { | 2185 | static 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); |
| 2191 | 2190 | ||
| 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; |
| 2200 | 2216 | ||
| 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 | } | ||
| 2202 | 2221 | ||
| 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 | } |
| 2227 | 2244 | ||
| 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); | ||
| 2231 | 2246 | ||
| 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); |
| 104 | static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope); | 104 | static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope); |
| 105 | static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope); | 105 | static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope); |
| 106 | static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name); | 106 | static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name); |
| 107 | static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope); | ||
| 108 | 107 | ||
| 109 | static TransScopeBlock *trans_scope_block_find(TransScope *scope); | 108 | static TransScopeBlock *trans_scope_block_find(TransScope *scope); |
| 110 | static TransScopeSwitch *trans_scope_switch_find(TransScope *scope); | ||
| 111 | 109 | ||
| 112 | static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl); | 110 | static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl); |
| 113 | static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl); | 111 | static 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 | } |
| 267 | 265 | ||
| 268 | static 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 | |||
| 274 | static 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 | |||
| 280 | static AstNode *trans_create_node_bool(Context *c, bool value) { | 266 | static 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 | } |
| 2381 | 2367 | ||
| 2382 | static 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 | |||
| 2443 | static 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 | |||
| 2486 | static 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 | |||
| 2521 | static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) { | 2368 | static 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 | } |
| 3873 | 3721 | ||
| 3874 | static 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 | |||
| 3882 | static TransScopeBlock *trans_scope_block_find(TransScope *scope) { | 3722 | static 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 | } |
| 3891 | 3731 | ||
| 3892 | static 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 | |||
| 3902 | static void render_aliases(Context *c) { | 3732 | static 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; |
| 245 | 245 | ||
| 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; |
| 248 | 248 | ||
| 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 { |
| 251 | 251 | ||
| 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 | } |
| 256 | 256 | ||
| 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 | } |
| 264 | 262 | ||
| 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 implementation | 903 | // TODO non-recursive implementation |
| 904 | pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void { | 904 | pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void { |
| 905 | start_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(); | ||
| 924 | 924 | ||
| 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(); |
| 927 | 927 | ||
| 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); |
| 934 | 934 | ||
| 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 | } |
| 940 | 941 | ||
| 941 | pub const Dir = struct { | 942 | pub 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 invalid | 989 | /// 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 | } |
| 996 | 997 | ||
| 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; | ||
| 1020 | 1021 | ||
| 1021 | const name = cstr.toSlice(&linux_entry.d_name); | 1022 | const name = cstr.toSlice(&linux_entry.d_name); |
| 1022 | 1023 | ||
| 1023 | // skip . and .. entries | 1024 | // 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 | } |
| 1027 | 1028 | ||
| 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 | }; |
| 1045 | 1047 |
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" { |
| 55 | 55 | ||
| 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 | |||
| 59 | test "break from outer for loop" { | ||
| 60 | testBreakOuter(); | ||
| 61 | comptime testBreakOuter(); | ||
| 62 | } | ||
| 63 | |||
| 64 | fn 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 | |||
| 76 | test "continue outer for loop" { | ||
| 77 | testContinueOuter(); | ||
| 78 | comptime testContinueOuter(); | ||
| 79 | } | ||
| 80 | |||
| 81 | fn 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 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | |||
| 3 | test "goto and labels" { | ||
| 4 | gotoLoop(); | ||
| 5 | assert(goto_counter == 10); | ||
| 6 | } | ||
| 7 | fn gotoLoop() { | ||
| 8 | var i: i32 = 0; | ||
| 9 | goto cond; | ||
| 10 | loop: | ||
| 11 | i += 1; | ||
| 12 | cond: | ||
| 13 | if (!(i < 10)) goto end; | ||
| 14 | goto_counter += 1; | ||
| 15 | goto loop; | ||
| 16 | end: | ||
| 17 | } | ||
| 18 | var goto_counter: i32 = 0; | ||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | test "goto leave defer scope" { | ||
| 23 | testGotoLeaveDeferScope(true); | ||
| 24 | } | ||
| 25 | fn testGotoLeaveDeferScope(b: bool) { | ||
| 26 | var it_worked = false; | ||
| 27 | |||
| 28 | goto entry; | ||
| 29 | exit: | ||
| 30 | if (it_worked) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | unreachable; | ||
| 34 | entry: | ||
| 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 | } |
| 190 | 190 | ||
| 191 | test "break from outer while loop" { | ||
| 192 | testBreakOuter(); | ||
| 193 | comptime testBreakOuter(); | ||
| 194 | } | ||
| 195 | |||
| 196 | fn testBreakOuter() { | ||
| 197 | outer: while (true) { | ||
| 198 | while (true) { | ||
| 199 | break :outer; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | test "continue outer while loop" { | ||
| 205 | testContinueOuter(); | ||
| 206 | comptime testContinueOuter(); | ||
| 207 | } | ||
| 208 | |||
| 209 | fn testContinueOuter() { | ||
| 210 | var i: usize = 0; | ||
| 211 | outer: while (i < 10) : (i += 1) { | ||
| 212 | while (true) { | ||
| 213 | continue :outer; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 191 | fn returnNull() -> ?i32 { null } | 218 | fn returnNull() -> ?i32 { null } |
| 192 | fn returnMaybe(x: i32) -> ?i32 { x } | 219 | fn returnMaybe(x: i32) -> ?i32 { x } |
| 193 | error YouWantedAnError; | 220 | error YouWantedAnError; |
test/compile_errors.zig+21-30| ... | @@ -1,6 +1,27 @@ | ... | @@ -1,6 +1,27 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: &tests.CompileErrorContext) { | 3 | pub 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"); |
| 835 | 856 | ||
| 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"); |
| 1856 | 1857 | ||
| 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 | ); |
| 1007 | 1007 | ||
| 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 | ); |
| 1087 | 1045 | ||
| 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; |