diff --git a/doc/langref.html.in b/doc/langref.html.in index e17e1ecd8f..faba4f8b10 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5847,11 +5847,9 @@ ParamDeclList = "(" list(ParamDecl, ",") ")" ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...") -Block = "{" many(Statement) option(Expression) "}" +Block = option(Symbol ":") "{" many(Statement) option(Expression) "}" -Statement = Label | LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" - -Label = Symbol ":" +Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" TypeExpr = PrefixOpExpression | "var" @@ -5891,13 +5889,13 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Sy SwitchItem = Expression | (Expression "..." Expression) -ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) +ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression ReturnExpression = option("%") "return" option(Expression) -BreakExpression = "break" option(Expression) +BreakExpression = "break" option(":" Symbol) option(Expression) Defer(body) = option("%") "defer" body @@ -5907,7 +5905,7 @@ TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body)) -WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) +WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression @@ -5955,15 +5953,13 @@ StructLiteralField = "." Symbol "=" Expression PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" -PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl +PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol)) ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr -GotoExpression = "goto" Symbol - GroupedExpression = "(" Expression ")" -KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable" +KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" ContainerDecl = option("extern" | "packed") ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression))) diff --git a/src/all_types.hpp b/src/all_types.hpp index 28477c8107..87541bb918 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -386,8 +386,6 @@ enum NodeType { NodeTypeSwitchExpr, NodeTypeSwitchProng, NodeTypeSwitchRange, - NodeTypeLabel, - NodeTypeGoto, NodeTypeCompTime, NodeTypeBreak, NodeTypeContinue, @@ -452,6 +450,7 @@ struct AstNodeParamDecl { }; struct AstNodeBlock { + Buf *name; ZigList statements; bool last_statement_is_result_expression; }; @@ -662,6 +661,7 @@ struct AstNodeTestExpr { }; struct AstNodeWhileExpr { + Buf *name; AstNode *condition; Buf *var_symbol; bool var_is_ptr; @@ -673,6 +673,7 @@ struct AstNodeWhileExpr { }; struct AstNodeForExpr { + Buf *name; AstNode *array_expr; AstNode *elem_node; // always a symbol AstNode *index_node; // always a symbol, might be null @@ -704,11 +705,6 @@ struct AstNodeLabel { Buf *name; }; -struct AstNodeGoto { - Buf *name; - bool is_inline; -}; - struct AstNodeCompTime { AstNode *expr; }; @@ -836,11 +832,14 @@ struct AstNodeBoolLiteral { }; struct AstNodeBreakExpr { + Buf *name; AstNode *expr; // may be null }; struct AstNodeContinueExpr { + Buf *name; }; + struct AstNodeUnreachableExpr { }; @@ -886,7 +885,6 @@ struct AstNode { AstNodeSwitchProng switch_prong; AstNodeSwitchRange switch_range; AstNodeLabel label; - AstNodeGoto goto_expr; AstNodeCompTime comptime_expr; AstNodeAsmExpr asm_expr; AstNodeFieldAccessExpr field_access_expr; @@ -1741,6 +1739,7 @@ struct ScopeCImport { struct ScopeLoop { Scope base; + Buf *name; IrBasicBlock *break_block; IrBasicBlock *continue_block; IrInstruction *is_comptime; diff --git a/src/analyze.cpp b/src/analyze.cpp index d6719cf52c..23301cc4de 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -144,9 +144,15 @@ ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) { } ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) { - assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr); ScopeLoop *scope = allocate(1); init_scope(&scope->base, ScopeIdLoop, node, parent); + if (node->type == NodeTypeWhileExpr) { + scope->name = node->data.while_expr.name; + } else if (node->type == NodeTypeForExpr) { + scope->name = node->data.for_expr.name; + } else { + zig_unreachable(); + } return scope; } @@ -2916,8 +2922,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: - case NodeTypeLabel: - case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeUnreachable: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index c22c16d90a..e64a19d42d 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -215,10 +215,6 @@ static const char *node_type_str(NodeType node_type) { return "SwitchProng"; case NodeTypeSwitchRange: return "SwitchRange"; - case NodeTypeLabel: - return "Label"; - case NodeTypeGoto: - return "Goto"; case NodeTypeCompTime: return "CompTime"; case NodeTypeBreak: @@ -391,7 +387,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { switch (node->type) { case NodeTypeSwitchProng: case NodeTypeSwitchRange: - case NodeTypeLabel: case NodeTypeStructValueField: zig_unreachable(); case NodeTypeRoot: @@ -470,6 +465,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { break; } case NodeTypeBlock: + if (node->data.block.name != nullptr) { + fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name)); + } if (node->data.block.statements.length == 0) { fprintf(ar->f, "{}"); break; @@ -478,13 +476,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { ar->indent += ar->indent_size; for (size_t i = 0; i < node->data.block.statements.length; i += 1) { AstNode *statement = node->data.block.statements.at(i); - if (statement->type == NodeTypeLabel) { - ar->indent -= ar->indent_size; - print_indent(ar); - fprintf(ar->f, "%s:\n", buf_ptr(statement->data.label.name)); - ar->indent += ar->indent_size; - continue; - } print_indent(ar); render_node_grouped(ar, statement); if (!(i == node->data.block.statements.length - 1 && @@ -515,6 +506,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypeBreak: { fprintf(ar->f, "break"); + if (node->data.break_expr.name != nullptr) { + fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name)); + } if (node->data.break_expr.expr) { fprintf(ar->f, " "); render_node_grouped(ar, node->data.break_expr.expr); @@ -828,6 +822,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } case NodeTypeWhileExpr: { + if (node->data.while_expr.name != nullptr) { + fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name)); + } const char *inline_str = node->data.while_expr.is_inline ? "inline " : ""; fprintf(ar->f, "%swhile (", inline_str); render_node_grouped(ar, node->data.while_expr.condition); @@ -957,11 +954,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, "}"); break; } - case NodeTypeGoto: - { - fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name)); - break; - } case NodeTypeCompTime: { fprintf(ar->f, "comptime "); @@ -970,6 +962,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } case NodeTypeForExpr: { + if (node->data.for_expr.name != nullptr) { + fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name)); + } const char *inline_str = node->data.for_expr.is_inline ? "inline " : ""; fprintf(ar->f, "%sfor (", inline_str); 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) { case NodeTypeContinue: { fprintf(ar->f, "continue"); + if (node->data.continue_expr.name != nullptr) { + fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name)); + } break; } case NodeTypeUnreachable: diff --git a/src/ir.cpp b/src/ir.cpp index db919350d1..d3dd58aaff 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3511,29 +3511,6 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s return var; } -static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) { - while (scope) { - if (scope->id == ScopeIdBlock) { - ScopeBlock *block_scope = (ScopeBlock *)scope; - auto entry = block_scope->label_table.maybe_get(name); - if (entry) - return entry->value; - } - scope = scope->parent; - } - - return nullptr; -} - -static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) { - while (scope) { - if (scope->id == ScopeIdBlock) - return (ScopeBlock *)scope; - scope = scope->parent; - } - return nullptr; -} - static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) { assert(block_node->type == NodeTypeBlock); @@ -3557,38 +3534,6 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { AstNode *statement_node = block_node->data.block.statements.at(i); - if (statement_node->type == NodeTypeLabel) { - Buf *label_name = statement_node->data.label.name; - IrBasicBlock *label_block = ir_build_basic_block(irb, child_scope, buf_ptr(label_name)); - LabelTableEntry *label = allocate(1); - label->decl_node = statement_node; - label->bb = label_block; - irb->exec->all_labels.append(label); - - LabelTableEntry *existing_label = find_label(irb->exec, child_scope, label_name); - if (existing_label) { - ErrorMsg *msg = add_node_error(irb->codegen, statement_node, - buf_sprintf("duplicate label name '%s'", buf_ptr(label_name))); - add_error_note(irb->codegen, msg, existing_label->decl_node, buf_sprintf("other label here")); - return irb->codegen->invalid_instruction; - } else { - ScopeBlock *scope_block = find_block_scope(irb->exec, child_scope); - scope_block->label_table.put(label_name, label); - } - - if (!is_continuation_unreachable) { - // fall through into new labeled basic block - IrInstruction *is_comptime = ir_mark_gen(ir_build_const_bool(irb, child_scope, statement_node, - ir_should_inline(irb->exec, child_scope))); - ir_mark_gen(ir_build_br(irb, child_scope, statement_node, label_block, is_comptime)); - } - ir_set_cursor_at_end(irb, label_block); - - // a label is an entry point - is_continuation_unreachable = false; - continue; - } - IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope); is_continuation_unreachable = instr_is_unreachable(statement_value); if (is_continuation_unreachable) { @@ -6000,22 +5945,6 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } -static IrInstruction *ir_gen_goto(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeGoto); - - // make a placeholder unreachable statement and a note to come back and - // replace the instruction with a branch instruction - IrGotoItem *goto_item = irb->exec->goto_list.add_one(); - goto_item->bb = irb->current_basic_block; - goto_item->instruction_index = irb->current_basic_block->instruction_list.length; - goto_item->source_node = node; - goto_item->scope = scope; - - // we don't know if we need to generate defer expressions yet - // we do that later when we find out which label we're jumping to. - return ir_build_unreachable(irb, scope, node); -} - static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeCompTime); @@ -6033,16 +5962,28 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * Scope *search_scope = break_scope; ScopeLoop *loop_scope; + bool saw_any_loop_scope = false; for (;;) { if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) { - add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop")); - return irb->codegen->invalid_instruction; + if (saw_any_loop_scope) { + add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.break_expr.name))); + return irb->codegen->invalid_instruction; + } else { + add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop")); + return irb->codegen->invalid_instruction; + } } else if (search_scope->id == ScopeIdDeferExpr) { add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression")); return irb->codegen->invalid_instruction; } else if (search_scope->id == ScopeIdLoop) { - loop_scope = (ScopeLoop *)search_scope; - break; + ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope; + saw_any_loop_scope = true; + if (node->data.break_expr.name == nullptr || + (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name))) + { + loop_scope = this_loop_scope; + break; + } } search_scope = search_scope->parent; } @@ -6081,16 +6022,28 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast Scope *search_scope = continue_scope; ScopeLoop *loop_scope; + bool saw_any_loop_scope = false; for (;;) { if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) { - add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop")); - return irb->codegen->invalid_instruction; + if (saw_any_loop_scope) { + add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name))); + return irb->codegen->invalid_instruction; + } else { + add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop")); + return irb->codegen->invalid_instruction; + } } else if (search_scope->id == ScopeIdDeferExpr) { add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression")); return irb->codegen->invalid_instruction; } else if (search_scope->id == ScopeIdLoop) { - loop_scope = (ScopeLoop *)search_scope; - break; + ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope; + saw_any_loop_scope = true; + if (node->data.continue_expr.name == nullptr || + (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name))) + { + loop_scope = this_loop_scope; + break; + } } search_scope = search_scope->parent; } @@ -6332,7 +6285,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeStructField: - case NodeTypeLabel: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeErrorValueDecl: @@ -6396,8 +6348,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); - case NodeTypeGoto: - return ir_lval_wrap(irb, scope, ir_gen_goto(irb, scope, node), lval); case NodeTypeCompTime: return ir_gen_comptime(irb, scope, node, lval); case NodeTypeErrorType: @@ -6432,70 +6382,6 @@ static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) { return ir_gen_node_extra(irb, node, scope, LVAL_NONE); } -static bool ir_goto_pass2(IrBuilder *irb) { - for (size_t i = 0; i < irb->exec->goto_list.length; i += 1) { - IrGotoItem *goto_item = &irb->exec->goto_list.at(i); - AstNode *source_node = goto_item->source_node; - - // Since a goto will always end a basic block, we move the "current instruction" - // index back to over the placeholder unreachable instruction and begin overwriting - irb->current_basic_block = goto_item->bb; - irb->current_basic_block->instruction_list.resize(goto_item->instruction_index); - - Buf *label_name = source_node->data.goto_expr.name; - - // Search up the scope until we find one of these things: - // * A block scope with the label in it => OK - // * A defer expression scope => error, error, cannot leave defer expression - // * Top level scope => error, didn't find label - - LabelTableEntry *label; - Scope *search_scope = goto_item->scope; - for (;;) { - if (search_scope == nullptr) { - add_node_error(irb->codegen, source_node, - buf_sprintf("no label in scope named '%s'", buf_ptr(label_name))); - return false; - } else if (search_scope->id == ScopeIdBlock) { - ScopeBlock *block_scope = (ScopeBlock *)search_scope; - auto entry = block_scope->label_table.maybe_get(label_name); - if (entry) { - label = entry->value; - break; - } - } else if (search_scope->id == ScopeIdDeferExpr) { - add_node_error(irb->codegen, source_node, - buf_sprintf("cannot goto out of defer expression")); - return false; - } - search_scope = search_scope->parent; - } - - label->used = true; - - IrInstruction *is_comptime = ir_build_const_bool(irb, goto_item->scope, source_node, - ir_should_inline(irb->exec, goto_item->scope) || source_node->data.goto_expr.is_inline); - if (!ir_gen_defers_for_block(irb, goto_item->scope, label->bb->scope, false)) { - add_node_error(irb->codegen, source_node, - buf_sprintf("no label in scope named '%s'", buf_ptr(label_name))); - return false; - } - ir_build_br(irb, goto_item->scope, source_node, label->bb, is_comptime); - } - - for (size_t i = 0; i < irb->exec->all_labels.length; i += 1) { - LabelTableEntry *label = irb->exec->all_labels.at(i); - if (!label->used) { - add_node_error(irb->codegen, label->decl_node, - buf_sprintf("label '%s' defined but not used", - buf_ptr(label->decl_node->data.label.name))); - return false; - } - } - - return true; -} - static void invalidate_exec(IrExecutable *exec) { if (exec->invalid) return; @@ -6532,11 +6418,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_mark_gen(ir_build_return(irb, scope, result->source_node, result)); } - if (!ir_goto_pass2(irb)) { - invalidate_exec(ir_executable); - return false; - } - return true; } diff --git a/src/parser.cpp b/src/parser.cpp index 579fe85f3b..b5fdd681e8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -632,27 +632,6 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m return node; } -/* -GotoExpression = "goto" Symbol -*/ -static AstNode *ast_parse_goto_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *goto_token = &pc->tokens->at(*token_index); - if (goto_token->id == TokenIdKeywordGoto) { - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, goto_token, TokenIdKeywordGoto); - zig_unreachable(); - } else { - return nullptr; - } - - AstNode *node = ast_create_node(pc, NodeTypeGoto, goto_token); - - Token *dest_symbol = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.goto_expr.name = token_buf(dest_symbol); - return node; -} - /* CompTimeExpression(body) = "comptime" body */ @@ -676,8 +655,8 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b } /* -PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl -KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable" +PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol)) +KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" */ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -721,6 +700,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo } else if (token->id == TokenIdKeywordContinue) { AstNode *node = ast_create_node(pc, NodeTypeContinue, token); *token_index += 1; + Token *maybe_colon_token = &pc->tokens->at(*token_index); + if (maybe_colon_token->id == TokenIdColon) { + *token_index += 1; + Token *name = ast_eat_token(pc, token_index, TokenIdSymbol); + node->data.continue_expr.name = token_buf(name); + } return node; } else if (token->id == TokenIdKeywordUndefined) { 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 return node; } - AstNode *goto_node = ast_parse_goto_expr(pc, token_index, false); - if (goto_node) - return goto_node; - AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); if (grouped_expr_node) { return grouped_expr_node; @@ -1488,7 +1469,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) { } /* -BreakExpression : "break" option(Expression) +BreakExpression = "break" option(":" Symbol) option(Expression) */ static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) { Token *token = &pc->tokens->at(*token_index); @@ -1498,8 +1479,15 @@ static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) { } else { return nullptr; } - AstNode *node = ast_create_node(pc, NodeTypeBreak, token); + + Token *maybe_colon_token = &pc->tokens->at(*token_index); + if (maybe_colon_token->id == TokenIdColon) { + *token_index += 1; + Token *name = ast_eat_token(pc, token_index, TokenIdSymbol); + node->data.break_expr.name = token_buf(name); + } + node->data.break_expr.expr = ast_parse_expression(pc, token_index, false); return node; @@ -1678,35 +1666,53 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, size_t *token_index, bo } /* -WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) +WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) */ static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *first_token = &pc->tokens->at(*token_index); - Token *while_token; + size_t orig_token_index = *token_index; - bool is_inline; - if (first_token->id == TokenIdKeywordInline) { - while_token = &pc->tokens->at(*token_index + 1); - if (while_token->id == TokenIdKeywordWhile) { - is_inline = true; - *token_index += 2; + Token *name_token = nullptr; + Token *token = &pc->tokens->at(*token_index); + + if (token->id == TokenIdSymbol) { + *token_index += 1; + Token *colon_token = &pc->tokens->at(*token_index); + if (colon_token->id == TokenIdColon) { + *token_index += 1; + name_token = token; + token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, while_token, TokenIdKeywordWhile); + ast_expect_token(pc, colon_token, TokenIdColon); zig_unreachable(); } else { + *token_index = orig_token_index; return nullptr; } - } else if (first_token->id == TokenIdKeywordWhile) { - while_token = first_token; - is_inline = false; + } + + bool is_inline = false; + if (token->id == TokenIdKeywordInline) { + is_inline = true; + *token_index += 1; + token = &pc->tokens->at(*token_index); + } + + Token *while_token; + if (token->id == TokenIdKeywordWhile) { + while_token = token; *token_index += 1; } else if (mandatory) { - ast_expect_token(pc, first_token, TokenIdKeywordWhile); + ast_expect_token(pc, token, TokenIdKeywordWhile); zig_unreachable(); } else { + *token_index = orig_token_index; return nullptr; } + AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token); + if (name_token != nullptr) { + node->data.while_expr.name = token_buf(name_token); + } node->data.while_expr.is_inline = is_inline; ast_eat_token(pc, token_index, TokenIdLParen); @@ -1766,36 +1772,53 @@ static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) { } /* -ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) +ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) */ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *first_token = &pc->tokens->at(*token_index); - Token *for_token; + size_t orig_token_index = *token_index; - bool is_inline; - if (first_token->id == TokenIdKeywordInline) { - is_inline = true; - for_token = &pc->tokens->at(*token_index + 1); - if (for_token->id == TokenIdKeywordFor) { - *token_index += 2; + Token *name_token = nullptr; + Token *token = &pc->tokens->at(*token_index); + + if (token->id == TokenIdSymbol) { + *token_index += 1; + Token *colon_token = &pc->tokens->at(*token_index); + if (colon_token->id == TokenIdColon) { + *token_index += 1; + name_token = token; + token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, first_token, TokenIdKeywordFor); + ast_expect_token(pc, colon_token, TokenIdColon); zig_unreachable(); } else { + *token_index = orig_token_index; return nullptr; } - } else if (first_token->id == TokenIdKeywordFor) { - for_token = first_token; - is_inline = false; + } + + bool is_inline = false; + if (token->id == TokenIdKeywordInline) { + is_inline = true; + *token_index += 1; + token = &pc->tokens->at(*token_index); + } + + Token *for_token; + if (token->id == TokenIdKeywordFor) { + for_token = token; *token_index += 1; } else if (mandatory) { - ast_expect_token(pc, first_token, TokenIdKeywordFor); + ast_expect_token(pc, token, TokenIdKeywordFor); zig_unreachable(); } else { + *token_index = orig_token_index; return nullptr; } AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token); + if (name_token != nullptr) { + node->data.for_expr.name = token_buf(name_token); + } node->data.for_expr.is_inline = is_inline; ast_eat_token(pc, token_index, TokenIdLParen); @@ -2125,32 +2148,6 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool /* Label: token(Symbol) token(Colon) */ -static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *symbol_token = &pc->tokens->at(*token_index); - if (symbol_token->id != TokenIdSymbol) { - if (mandatory) { - ast_expect_token(pc, symbol_token, TokenIdSymbol); - } else { - return nullptr; - } - } - - Token *colon_token = &pc->tokens->at(*token_index + 1); - if (colon_token->id != TokenIdColon) { - if (mandatory) { - ast_expect_token(pc, colon_token, TokenIdColon); - } else { - return nullptr; - } - } - - *token_index += 2; - - AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token); - node->data.label.name = token_buf(symbol_token); - return node; -} - static bool statement_terminates_without_semicolon(AstNode *node) { switch (node->type) { case NodeTypeIfBoolExpr: @@ -2175,7 +2172,6 @@ static bool statement_terminates_without_semicolon(AstNode *node) { return node->data.defer.expr->type == NodeTypeBlock; case NodeTypeSwitchExpr: case NodeTypeBlock: - case NodeTypeLabel: return true; default: return false; @@ -2183,27 +2179,48 @@ static bool statement_terminates_without_semicolon(AstNode *node) { } /* -Block = "{" many(Statement) option(Expression) "}" +Block = option(Symbol ":") "{" many(Statement) option(Expression) "}" Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl */ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) { + size_t orig_token_index = *token_index; + + Token *name_token = nullptr; Token *last_token = &pc->tokens->at(*token_index); + if (last_token->id == TokenIdSymbol) { + *token_index += 1; + Token *colon_token = &pc->tokens->at(*token_index); + if (colon_token->id == TokenIdColon) { + *token_index += 1; + name_token = last_token; + last_token = &pc->tokens->at(*token_index); + } else if (mandatory) { + ast_expect_token(pc, colon_token, TokenIdColon); + zig_unreachable(); + } else { + *token_index = orig_token_index; + return nullptr; + } + } + if (last_token->id != TokenIdLBrace) { if (mandatory) { ast_expect_token(pc, last_token, TokenIdLBrace); } else { + *token_index = orig_token_index; return nullptr; } } *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token); + if (name_token != nullptr) { + node->data.block.name = token_buf(name_token); + } for (;;) { - AstNode *statement_node = ast_parse_label(pc, token_index, false); - if (!statement_node) - statement_node = ast_parse_local_var_decl(pc, token_index); + AstNode *statement_node = ast_parse_local_var_decl(pc, token_index); if (!statement_node) statement_node = ast_parse_defer_expr(pc, token_index); if (!statement_node) @@ -2225,9 +2242,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand } } - node->data.block.last_statement_is_result_expression = statement_node && !( - statement_node->type == NodeTypeLabel || - statement_node->type == NodeTypeDefer); + node->data.block.last_statement_is_result_expression = statement_node && statement_node->type != NodeTypeDefer; last_token = &pc->tokens->at(*token_index); if (last_token->id == TokenIdRBrace) { @@ -2860,12 +2875,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.switch_range.start, visit, context); visit_field(&node->data.switch_range.end, visit, context); break; - case NodeTypeLabel: - // none - break; - case NodeTypeGoto: - // none - break; case NodeTypeCompTime: visit_field(&node->data.comptime_expr.expr, visit, context); break; diff --git a/src/translate_c.cpp b/src/translate_c.cpp index eba594e085..bb319ccb54 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -104,10 +104,8 @@ static TransScopeRoot *trans_scope_root_create(Context *c); static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope); static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope); static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name); -static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope); static TransScopeBlock *trans_scope_block_find(TransScope *scope); -static TransScopeSwitch *trans_scope_switch_find(TransScope *scope); static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl); 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 return node; } -static AstNode *trans_create_node_goto(Context *c, Buf *label_name) { - AstNode *goto_node = trans_create_node(c, NodeTypeGoto); - goto_node->data.goto_expr.name = label_name; - return goto_node; -} - -static AstNode *trans_create_node_label(Context *c, Buf *label_name) { - AstNode *label_node = trans_create_node(c, NodeTypeLabel); - label_node->data.label.name = label_name; - return label_node; -} - static AstNode *trans_create_node_bool(Context *c, bool value) { AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral); bool_node->data.bool_literal.value = value; @@ -2379,145 +2365,6 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt return while_scope->node; } -static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) { - TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope); - - TransScopeSwitch *switch_scope; - - const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt(); - if (var_decl_stmt == nullptr) { - switch_scope = trans_scope_switch_create(c, &block_scope->base); - } else { - AstNode *vars_node; - TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node); - if (var_scope == nullptr) - return nullptr; - if (vars_node != nullptr) - block_scope->node->data.block.statements.append(vars_node); - switch_scope = trans_scope_switch_create(c, var_scope); - } - block_scope->node->data.block.statements.append(switch_scope->switch_node); - - // TODO avoid name collisions - Buf *end_label_name = buf_create_from_str("end"); - switch_scope->end_label_name = end_label_name; - - const Expr *cond_expr = stmt->getCond(); - assert(cond_expr != nullptr); - - AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue); - if (expr_node == nullptr) - return nullptr; - switch_scope->switch_node->data.switch_expr.expr = expr_node; - - AstNode *body_node; - const Stmt *body_stmt = stmt->getBody(); - if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) { - if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt, - block_scope->node, nullptr)) - { - return nullptr; - } - } else { - TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node); - if (body_scope == nullptr) - return nullptr; - if (body_node != nullptr) - block_scope->node->data.block.statements.append(body_node); - } - - if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) { - AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); - prong_node->data.switch_prong.expr = trans_create_node_goto(c, end_label_name); - switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); - } - - // This is necessary if the last switch case "falls through" the end of the switch block - block_scope->node->data.block.statements.append(trans_create_node_goto(c, end_label_name)); - - block_scope->node->data.block.statements.append(trans_create_node_label(c, end_label_name)); - - return block_scope->node; -} - -static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node, - TransScope **out_scope) -{ - *out_node = nullptr; - - if (stmt->getRHS() != nullptr) { - emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension"); - return ErrorUnexpected; - } - - TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope); - assert(switch_scope != nullptr); - - Buf *label_name = buf_sprintf("case_%" PRIu32, switch_scope->case_index); - switch_scope->case_index += 1; - - { - // Add the prong - AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); - AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue); - if (item_node == nullptr) - return ErrorUnexpected; - prong_node->data.switch_prong.items.append(item_node); - - prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name); - - switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); - } - - TransScopeBlock *scope_block = trans_scope_block_find(parent_scope); - scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name)); - - AstNode *sub_stmt_node; - TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node); - if (new_scope == nullptr) - return ErrorUnexpected; - if (sub_stmt_node != nullptr) - scope_block->node->data.block.statements.append(sub_stmt_node); - - *out_scope = new_scope; - return ErrorNone; -} - -static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node, - TransScope **out_scope) -{ - *out_node = nullptr; - - TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope); - assert(switch_scope != nullptr); - - Buf *label_name = buf_sprintf("default"); - - { - // Add the prong - AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); - - prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name); - - switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); - switch_scope->found_default = true; - } - - TransScopeBlock *scope_block = trans_scope_block_find(parent_scope); - scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name)); - - - AstNode *sub_stmt_node; - TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node); - if (new_scope == nullptr) - return ErrorUnexpected; - if (sub_stmt_node != nullptr) - scope_block->node->data.block.statements.append(sub_stmt_node); - - *out_scope = new_scope; - return ErrorNone; -} - static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) { AstNode *loop_block_node; TransScopeWhile *while_scope; @@ -2595,8 +2442,7 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt if (cur_scope->id == TransScopeIdWhile) { return trans_create_node(c, NodeTypeBreak); } else if (cur_scope->id == TransScopeIdSwitch) { - TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope; - return trans_create_node_goto(c, switch_scope->end_label_name); + zig_panic("TODO"); } cur_scope = cur_scope->parent; } @@ -2696,12 +2542,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, return wrap_stmt(out_node, out_child_scope, scope, trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue)); case Stmt::SwitchStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_switch_stmt(c, scope, (const SwitchStmt *)stmt)); + emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass"); + return ErrorUnexpected; case Stmt::CaseStmtClass: - return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope); + emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass"); + return ErrorUnexpected; case Stmt::DefaultStmtClass: - return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope); + emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass"); + return ErrorUnexpected; case Stmt::NoStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass"); return ErrorUnexpected; @@ -3871,14 +3719,6 @@ static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scop return result; } -static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) { - TransScopeSwitch *result = allocate(1); - result->base.id = TransScopeIdSwitch; - result->base.parent = parent_scope; - result->switch_node = trans_create_node(c, NodeTypeSwitchExpr); - return result; -} - static TransScopeBlock *trans_scope_block_find(TransScope *scope) { while (scope != nullptr) { if (scope->id == TransScopeIdBlock) { @@ -3889,16 +3729,6 @@ static TransScopeBlock *trans_scope_block_find(TransScope *scope) { return nullptr; } -static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) { - while (scope != nullptr) { - if (scope->id == TransScopeIdSwitch) { - return (TransScopeSwitch *)scope; - } - scope = scope->parent; - } - return nullptr; -} - static void render_aliases(Context *c) { for (size_t i = 0; i < c->aliases.length; i += 1) { Alias *alias = &c->aliases.at(i); diff --git a/std/elf.zig b/std/elf.zig index 2c5b10b3f2..60b0119894 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -243,7 +243,7 @@ pub const Elf = struct { var file_stream = io.FileInStream.init(elf.in_file); const in = &file_stream.stream; - for (elf.section_headers) |*elf_section| { + section_loop: for (elf.section_headers) |*elf_section| { if (elf_section.sh_type == SHT_NULL) continue; const name_offset = elf.string_section.offset + elf_section.name; @@ -251,15 +251,13 @@ pub const Elf = struct { for (name) |expected_c| { const target_c = %return in.readByte(); - if (target_c == 0 or expected_c != target_c) goto next_section; + if (target_c == 0 or expected_c != target_c) continue :section_loop; } { const null_byte = %return in.readByte(); if (null_byte == 0) return elf_section; } - - next_section: } return null; diff --git a/std/os/index.zig b/std/os/index.zig index d26daed9fe..3eba15ef8a 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -902,40 +902,41 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void { /// this function recursively removes its entries and then tries again. // TODO non-recursive implementation pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void { -start_over: - // First, try deleting the item as a file. This way we don't follow sym links. - if (deleteFile(allocator, full_path)) { - return; - } else |err| { - if (err == error.FileNotFound) + start_over: while (true) { + // First, try deleting the item as a file. This way we don't follow sym links. + if (deleteFile(allocator, full_path)) { return; - if (err != error.IsDir) - return err; - } - { - var dir = Dir.open(allocator, full_path) %% |err| { + } else |err| { if (err == error.FileNotFound) return; - if (err == error.NotDir) - goto start_over; - return err; - }; - defer dir.close(); - - var full_entry_buf = ArrayList(u8).init(allocator); - defer full_entry_buf.deinit(); - - while (%return dir.next()) |entry| { - %return full_entry_buf.resize(full_path.len + entry.name.len + 1); - const full_entry_path = full_entry_buf.toSlice(); - mem.copy(u8, full_entry_path, full_path); - full_entry_path[full_path.len] = '/'; - mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name); - - %return deleteTree(allocator, full_entry_path); + if (err != error.IsDir) + return err; } + { + var dir = Dir.open(allocator, full_path) %% |err| { + if (err == error.FileNotFound) + return; + if (err == error.NotDir) + continue :start_over; + return err; + }; + defer dir.close(); + + var full_entry_buf = ArrayList(u8).init(allocator); + defer full_entry_buf.deinit(); + + while (%return dir.next()) |entry| { + %return full_entry_buf.resize(full_path.len + entry.name.len + 1); + const full_entry_path = full_entry_buf.toSlice(); + mem.copy(u8, full_entry_path, full_path); + full_entry_path[full_path.len] = '/'; + mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name); + + %return deleteTree(allocator, full_entry_path); + } + } + return deleteDir(allocator, full_path); } - return deleteDir(allocator, full_path); } pub const Dir = struct { @@ -988,58 +989,59 @@ pub const Dir = struct { /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to next, as well as when this ::Dir is deinitialized. pub fn next(self: &Dir) -> %?Entry { - start_over: - if (self.index >= self.end_index) { - if (self.buf.len == 0) { - self.buf = %return self.allocator.alloc(u8, page_size); - } - - while (true) { - const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len); - const err = linux.getErrno(result); - if (err > 0) { - switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, - posix.EINVAL => { - self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2); - continue; - }, - else => return unexpectedErrorPosix(err), - }; + start_over: while (true) { + if (self.index >= self.end_index) { + if (self.buf.len == 0) { + self.buf = %return self.allocator.alloc(u8, page_size); + } + + while (true) { + const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len); + const err = linux.getErrno(result); + if (err > 0) { + switch (err) { + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EINVAL => { + self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2); + continue; + }, + else => return unexpectedErrorPosix(err), + }; + } + if (result == 0) + return null; + self.index = 0; + self.end_index = result; + break; } - if (result == 0) - return null; - self.index = 0; - self.end_index = result; - break; } + const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]); + const next_index = self.index + linux_entry.d_reclen; + self.index = next_index; + + const name = cstr.toSlice(&linux_entry.d_name); + + // skip . and .. entries + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const type_char = self.buf[next_index - 1]; + const entry_kind = switch (type_char) { + posix.DT_BLK => Entry.Kind.BlockDevice, + posix.DT_CHR => Entry.Kind.CharacterDevice, + posix.DT_DIR => Entry.Kind.Directory, + posix.DT_FIFO => Entry.Kind.NamedPipe, + posix.DT_LNK => Entry.Kind.SymLink, + posix.DT_REG => Entry.Kind.File, + posix.DT_SOCK => Entry.Kind.UnixDomainSocket, + else => Entry.Kind.Unknown, + }; + return Entry { + .name = name, + .kind = entry_kind, + }; } - const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]); - const next_index = self.index + linux_entry.d_reclen; - self.index = next_index; - - const name = cstr.toSlice(&linux_entry.d_name); - - // skip . and .. entries - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - goto start_over; - } - - const type_char = self.buf[next_index - 1]; - const entry_kind = switch (type_char) { - posix.DT_BLK => Entry.Kind.BlockDevice, - posix.DT_CHR => Entry.Kind.CharacterDevice, - posix.DT_DIR => Entry.Kind.Directory, - posix.DT_FIFO => Entry.Kind.NamedPipe, - posix.DT_LNK => Entry.Kind.SymLink, - posix.DT_REG => Entry.Kind.File, - posix.DT_SOCK => Entry.Kind.UnixDomainSocket, - else => Entry.Kind.Unknown, - }; - return Entry { - .name = name, - .kind = entry_kind, - }; } }; diff --git a/test/behavior.zig b/test/behavior.zig index ecb8cf74c9..96a323a6c8 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -20,7 +20,6 @@ comptime { _ = @import("cases/fn.zig"); _ = @import("cases/for.zig"); _ = @import("cases/generics.zig"); - _ = @import("cases/goto.zig"); _ = @import("cases/if.zig"); _ = @import("cases/import.zig"); _ = @import("cases/incomplete_struct_param_tld.zig"); diff --git a/test/cases/for.zig b/test/cases/for.zig index e10e7acaca..e4b8094cf5 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -55,3 +55,37 @@ test "basic for loop" { assert(mem.eql(u8, buffer[0..buf_index], expected_result)); } + +test "break from outer for loop" { + testBreakOuter(); + comptime testBreakOuter(); +} + +fn testBreakOuter() { + var array = "aoeu"; + var count: usize = 0; + outer: for (array) |_| { + for (array) |_2| { // TODO shouldn't get error for redeclaring "_" + count += 1; + break :outer; + } + } + assert(count == 1); +} + +test "continue outer for loop" { + testContinueOuter(); + comptime testContinueOuter(); +} + +fn testContinueOuter() { + var array = "aoeu"; + var counter: usize = 0; + outer: for (array) |_| { + for (array) |_2| { // TODO shouldn't get error for redeclaring "_" + counter += 1; + continue :outer; + } + } + assert(counter == array.len); +} diff --git a/test/cases/goto.zig b/test/cases/goto.zig deleted file mode 100644 index 7713bc14aa..0000000000 --- a/test/cases/goto.zig +++ /dev/null @@ -1,37 +0,0 @@ -const assert = @import("std").debug.assert; - -test "goto and labels" { - gotoLoop(); - assert(goto_counter == 10); -} -fn gotoLoop() { - var i: i32 = 0; - goto cond; -loop: - i += 1; -cond: - if (!(i < 10)) goto end; - goto_counter += 1; - goto loop; -end: -} -var goto_counter: i32 = 0; - - - -test "goto leave defer scope" { - testGotoLeaveDeferScope(true); -} -fn testGotoLeaveDeferScope(b: bool) { - var it_worked = false; - - goto entry; -exit: - if (it_worked) { - return; - } - unreachable; -entry: - defer it_worked = true; - if (b) goto exit; -} diff --git a/test/cases/while.zig b/test/cases/while.zig index 33833cecfa..c16171d4a3 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -188,6 +188,33 @@ test "while on bool with else result follow break prong" { assert(result == 10); } +test "break from outer while loop" { + testBreakOuter(); + comptime testBreakOuter(); +} + +fn testBreakOuter() { + outer: while (true) { + while (true) { + break :outer; + } + } +} + +test "continue outer while loop" { + testContinueOuter(); + comptime testContinueOuter(); +} + +fn testContinueOuter() { + var i: usize = 0; + outer: while (i < 10) : (i += 1) { + while (true) { + continue :outer; + } + } +} + fn returnNull() -> ?i32 { null } fn returnMaybe(x: i32) -> ?i32 { x } error YouWantedAnError; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index fb7daea481..3446acda02 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,27 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) { + cases.add("labeled break not found", + \\export fn entry() { + \\ blah: while (true) { + \\ while (true) { + \\ break :outer; + \\ } + \\ } + \\} + , ".tmp_source.zig:4:13: error: labeled loop not found: 'outer'"); + + cases.add("labeled continue not found", + \\export fn entry() { + \\ var i: usize = 0; + \\ blah: while (i < 10) : (i += 1) { + \\ while (true) { + \\ continue :outer; + \\ } + \\ } + \\} + , ".tmp_source.zig:5:13: error: labeled loop not found: 'outer'"); + cases.add("attempt to use 0 bit type in extern fn", \\extern fn foo(ptr: extern fn(&void)); \\ @@ -833,26 +854,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\export fn entry() -> usize { @sizeOf(@typeOf(test1)) } , ".tmp_source.zig:3:16: error: unable to evaluate constant expression"); - cases.add("goto jumping into block", - \\export fn f() { - \\ { - \\a_label: - \\ } - \\ goto a_label; - \\} - , ".tmp_source.zig:5:5: error: no label in scope named 'a_label'"); - - cases.add("goto jumping past a defer", - \\fn f(b: bool) { - \\ if (b) goto label; - \\ defer derp(); - \\label: - \\} - \\fn derp(){} - \\ - \\export fn entry() -> usize { @sizeOf(@typeOf(f)) } - , ".tmp_source.zig:2:12: error: no label in scope named 'label'"); - cases.add("assign null to non-nullable pointer", \\const a: &u8 = null; \\ @@ -1854,16 +1855,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:4:13: error: cannot continue out of defer expression"); - cases.add("cannot goto out of defer expression", - \\export fn foo() { - \\ defer { - \\ goto label; - \\ }; - \\label: - \\} - , - ".tmp_source.zig:3:9: error: cannot goto out of defer expression"); - cases.add("calling a var args function only known at runtime", \\var foos = []fn(...) { foo1, foo2 }; \\ diff --git a/test/translate_c.zig b/test/translate_c.zig index aff2140f8d..4f545139e7 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1005,48 +1005,6 @@ pub fn addCases(cases: &tests.TranslateCContext) { \\} ); - cases.add("switch statement", - \\int foo(int x) { - \\ switch (x) { - \\ case 1: - \\ x += 1; - \\ case 2: - \\ break; - \\ case 3: - \\ case 4: - \\ return x + 1; - \\ default: - \\ return 10; - \\ } - \\ return x + 13; - \\} - , - \\fn foo(_arg_x: c_int) -> c_int { - \\ var x = _arg_x; - \\ { - \\ switch (x) { - \\ 1 => goto case_0, - \\ 2 => goto case_1, - \\ 3 => goto case_2, - \\ 4 => goto case_3, - \\ else => goto default, - \\ }; - \\ case_0: - \\ x += 1; - \\ case_1: - \\ goto end; - \\ case_2: - \\ case_3: - \\ return x + 1; - \\ default: - \\ return 10; - \\ goto end; - \\ end: - \\ }; - \\ return x + 13; - \\} - ); - cases.add("macros with field targets", \\typedef unsigned int GLbitfield; \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask); @@ -1085,44 +1043,6 @@ pub fn addCases(cases: &tests.TranslateCContext) { \\pub const OpenGLProcs = union_OpenGLProcs; ); - cases.add("switch statement with no default", - \\int foo(int x) { - \\ switch (x) { - \\ case 1: - \\ x += 1; - \\ case 2: - \\ break; - \\ case 3: - \\ case 4: - \\ return x + 1; - \\ } - \\ return x + 13; - \\} - , - \\fn foo(_arg_x: c_int) -> c_int { - \\ var x = _arg_x; - \\ { - \\ switch (x) { - \\ 1 => goto case_0, - \\ 2 => goto case_1, - \\ 3 => goto case_2, - \\ 4 => goto case_3, - \\ else => goto end, - \\ }; - \\ case_0: - \\ x += 1; - \\ case_1: - \\ goto end; - \\ case_2: - \\ case_3: - \\ return x + 1; - \\ goto end; - \\ end: - \\ }; - \\ return x + 13; - \\} - ); - cases.add("variable name shadowing", \\int foo(void) { \\ int x = 1;