diff --git a/doc/langref.md b/doc/langref.md index 98cd0e4cc4..32761f23e5 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -146,7 +146,7 @@ ArrayAccessExpression : token(LBracket) Expression token(RBracket) PrefixOp : token(Not) | token(Dash) | token(Tilde) | (token(Ampersand) option(token(Const))) -PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | Break | BlockExpression | token(Symbol) | StructValueExpression +PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace) @@ -154,8 +154,6 @@ StructValueExpressionField : token(Dot) token(Symbol) token(Eq) Expression Goto: token(Goto) token(Symbol) -Break: token(Break) - GroupedExpression : token(LParen) Expression token(RParen) KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) diff --git a/src/analyze.cpp b/src/analyze.cpp index b53259bac5..aa747b5de1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -49,6 +49,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: + case NodeTypeContinue: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructDecl: @@ -532,6 +533,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: + case NodeTypeContinue: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: @@ -601,6 +603,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: + case NodeTypeContinue: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: @@ -1381,6 +1384,16 @@ static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, return g->builtin_types.entry_unreachable; } +static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + if (!context->break_allowed) { + add_node_error(g, node, + buf_sprintf("'continue' expression not in loop")); + } + return g->builtin_types.entry_unreachable; +} + static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -1463,6 +1476,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeBreak: return_type = analyze_break_expr(g, import, context, expected_type, node); break; + case NodeTypeContinue: + return_type = analyze_continue_expr(g, import, context, expected_type, node); + break; case NodeTypeAsmExpr: { node->data.asm_expr.return_count = 0; @@ -1817,6 +1833,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: + case NodeTypeContinue: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: diff --git a/src/analyze.hpp b/src/analyze.hpp index 652788dcc7..81180402ca 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -197,6 +197,7 @@ struct CodeGen { LLVMBasicBlockRef cur_basic_block; BlockContext *cur_block_context; ZigList break_block_stack; + ZigList continue_block_stack; bool c_stdint_used; AstNode *root_export_decl; int version_major; diff --git a/src/codegen.cpp b/src/codegen.cpp index b20eb8801c..f7ebe6f9e1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1025,8 +1025,10 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, body_block); g->break_block_stack.append(end_block); + g->continue_block_stack.append(cond_block); gen_expr(g, node->data.while_expr.body); g->break_block_stack.pop(); + g->continue_block_stack.pop(); if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) { LLVMBuildBr(g->builder, cond_block); } @@ -1043,6 +1045,14 @@ static LLVMValueRef gen_break(CodeGen *g, AstNode *node) { return LLVMBuildBr(g->builder, dest_block); } +static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeContinue); + LLVMBasicBlockRef dest_block = g->continue_block_stack.last(); + + add_debug_source_node(g, node); + return LLVMBuildBr(g->builder, dest_block); +} + static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { switch (node->type) { case NodeTypeBinOpExpr: @@ -1174,6 +1184,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); case NodeTypeBreak: return gen_break(g, node); + case NodeTypeContinue: + return gen_continue(g, node); case NodeTypeLabel: { LabelTableEntry *label_entry = node->codegen_node->data.label_entry; diff --git a/src/parser.cpp b/src/parser.cpp index ff6a2dd573..994923c377 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -124,6 +124,8 @@ const char *node_type_str(NodeType node_type) { return "Goto"; case NodeTypeBreak: return "Break"; + case NodeTypeContinue: + return "Continue"; case NodeTypeAsmExpr: return "AsmExpr"; case NodeTypeFieldAccessExpr: @@ -341,6 +343,9 @@ void ast_print(AstNode *node, int indent) { case NodeTypeBreak: fprintf(stderr, "%s\n", node_type_str(node->type)); break; + case NodeTypeContinue: + fprintf(stderr, "%s\n", node_type_str(node->type)); + break; case NodeTypeAsmExpr: fprintf(stderr, "%s\n", node_type_str(node->type)); break; @@ -1107,7 +1112,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { } /* -PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | Break | BlockExpression | token(Symbol) | StructValueExpression +PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression */ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1165,6 +1170,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool AstNode *node = ast_create_node(pc, NodeTypeBreak, token); *token_index += 1; return node; + } else if (token->id == TokenIdKeywordContinue) { + AstNode *node = ast_create_node(pc, NodeTypeContinue, token); + *token_index += 1; + return node; } AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); diff --git a/src/parser.hpp b/src/parser.hpp index 309337bbfa..a9ed03812d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -49,6 +49,7 @@ enum NodeType { NodeTypeLabel, NodeTypeGoto, NodeTypeBreak, + NodeTypeContinue, NodeTypeAsmExpr, NodeTypeStructDecl, NodeTypeStructField, diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 165113ebe9..f1e8555903 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -659,17 +659,15 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { } )SOURCE", "loop\nloop\nloop\nloop\n"); - add_simple_case("break out of while loop", R"SOURCE( + add_simple_case("continue and break", R"SOURCE( use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { var i : i32 = 0; while true { - while true { - if i >= 4 { - break; - } - print_str("loop\n"); - i += 1; + print_str("loop\n"); + i += 1; + if i < 4 { + continue; } break; } @@ -678,6 +676,8 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { )SOURCE", "loop\nloop\nloop\nloop\n"); } +//////////////////////////////////////////////////////////////////////////////////// + static void add_compile_failure_test_cases(void) { add_compile_fail_case("multiple function definitions", R"SOURCE( fn a() {} @@ -958,6 +958,12 @@ fn f() { break; } )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression not in loop"); + + add_compile_fail_case("invalid continue expression", R"SOURCE( +fn f() { + continue; +} + )SOURCE", 1, ".tmp_source.zig:3:5: error: 'continue' expression not in loop"); } static void print_compiler_invocation(TestCase *test_case) {