From f8ca6c70c74db6e6f0d4462aa763adb6f1f41c7e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Dec 2015 00:47:35 -0700 Subject: [PATCH] add labels and goto --- README.md | 9 +++-- doc/vim/syntax/zig.vim | 4 +- example/hello_world/hello.zig | 13 ++++++- src/analyze.cpp | 72 +++++++++++++++++++++++++++++++---- src/codegen.cpp | 39 +++++++++++++++++-- src/main.cpp | 13 ++++++- src/parser.cpp | 69 +++++++++++++++++++++++++++++---- src/parser.hpp | 12 ++++++ src/semantic_info.hpp | 10 ++++- src/tokenizer.cpp | 3 ++ src/tokenizer.hpp | 1 + src/zig_llvm.cpp | 13 +++++++ src/zig_llvm.hpp | 4 ++ test/run_tests.cpp | 35 +++++++++++++++++ 14 files changed, 270 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 38a871d2f0..49f9135a50 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,6 @@ make * variable declarations and assignment expressions * Type checking * loops - * labels and goto * inline assembly and syscalls * conditional compilation and ability to check target platform and architecture * main function with command line arguments @@ -110,7 +109,9 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) -Statement : NonBlockExpression token(Semicolon) | BlockExpression +Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression + +Label: token(Symbol) token(Colon) Expression : BlockExpression | NonBlockExpression @@ -162,7 +163,9 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma) PrefixOp : token(Not) | token(Dash) | token(Tilde) -PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) +PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto + +Goto: token(Goto) token(Symbol) GroupedExpression : token(LParen) Expression token(RParen) ``` diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index 3804421849..c8a4918cc6 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -1,13 +1,13 @@ " Vim syntax file " Language: Zig " Maintainer: Andrew Kelley -" Latest Revision: 27 November 2015 +" Latest Revision: 02 December 2015 if exists("b:current_syntax") finish endif -syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void +syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void goto syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index d950d9aa17..8742f90c4e 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -6,7 +6,18 @@ extern { fn exit(code: i32) -> unreachable; } +fn loop(a : i32) { + if a == 0 { + goto done; + } + puts("loop"); + loop(a - 1); + +done: + return; +} + export fn _start() -> unreachable { - puts("Hello, world!"); + loop(3); exit(0); } diff --git a/src/analyze.cpp b/src/analyze.cpp index ef3b9ff760..c7f31d937f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -131,6 +131,25 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t resolve_type(g, node->data.fn_proto.return_type); } +static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) { + assert(node->type == NodeTypeBlock); + + for (int i = 0; i < node->data.block.statements.length; i += 1) { + AstNode *label_node = node->data.block.statements.at(i); + if (label_node->type != NodeTypeLabel) + continue; + + LabelTableEntry *label_entry = allocate(1); + label_entry->label_node = label_node; + Buf *name = &label_node->data.label.name; + fn_table_entry->label_table.put(name, label_entry); + + assert(!label_node->codegen_node); + label_node->codegen_node = allocate(1); + label_node->codegen_node->data.label_entry = label_entry; + } +} + static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) { switch (node->type) { case NodeTypeExternBlock: @@ -158,6 +177,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, fn_table_entry->calling_convention = LLVMCCallConv; fn_table_entry->import_entry = import; fn_table_entry->symbol_table.init(8); + fn_table_entry->label_table.init(8); resolve_function_proto(g, fn_proto, fn_table_entry); @@ -208,6 +228,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, fn_table_entry->internal_linkage = is_internal; fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv; fn_table_entry->symbol_table.init(8); + fn_table_entry->label_table.init(8); g->fn_protos.append(fn_table_entry); g->fn_defs.append(fn_table_entry); @@ -222,6 +243,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, assert(!proto_node->codegen_node); proto_node->codegen_node = allocate(1); proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry; + + preview_function_labels(g, node->data.fn_def.body, fn_table_entry); } } break; @@ -290,6 +313,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, case NodeTypeCastExpr: case NodeTypePrefixOpExpr: case NodeTypeIfExpr: + case NodeTypeLabel: + case NodeTypeGoto: zig_unreachable(); } } @@ -339,6 +364,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, return_type = g->builtin_types.entry_void; for (int i = 0; i < node->data.block.statements.length; i += 1) { AstNode *child = node->data.block.statements.at(i); + if (child->type == NodeTypeLabel) + continue; if (return_type == g->builtin_types.entry_unreachable) { if (child->type == NodeTypeVoid) { // {unreachable;void;void} is allowed. @@ -365,7 +392,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, if (actual_return_type == g->builtin_types.entry_unreachable) { // "return exit(0)" should just be "exit(0)". - add_node_error(g, node, buf_sprintf("returning is unreachable.")); + add_node_error(g, node, buf_sprintf("returning is unreachable")); actual_return_type = g->builtin_types.entry_invalid; } @@ -373,7 +400,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, return_type = g->builtin_types.entry_unreachable; break; } - case NodeTypeVariableDeclaration: { zig_panic("TODO: analyze variable declaration"); @@ -382,6 +408,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, break; } + case NodeTypeGoto: + { + FnTableEntry *fn_table_entry = get_context_fn_entry(context); + auto table_entry = fn_table_entry->label_table.maybe_get(&node->data.go_to.name); + if (table_entry) { + assert(!node->codegen_node); + node->codegen_node = allocate(1); + node->codegen_node->data.label_entry = table_entry->value; + } else { + add_node_error(g, node, + buf_sprintf("use of undeclared label '%s'", buf_ptr(&node->data.go_to.name))); + } + return_type = g->builtin_types.entry_unreachable; + break; + } case NodeTypeBinOpExpr: { switch (node->data.bin_op_expr.bin_op) { @@ -563,8 +604,18 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, node->data.if_expr.then_block); - check_type_compatibility(g, node, expected_type, else_type); - return_type = then_type; + TypeTableEntry *primary_type; + TypeTableEntry *other_type; + if (then_type == g->builtin_types.entry_unreachable) { + primary_type = else_type; + other_type = then_type; + } else { + primary_type = then_type; + other_type = else_type; + } + + check_type_compatibility(g, node, expected_type, other_type); + return_type = primary_type; break; } case NodeTypeDirective: @@ -577,14 +628,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeExternBlock: case NodeTypeFnDef: case NodeTypeUse: + case NodeTypeLabel: zig_unreachable(); } assert(return_type); check_type_compatibility(g, node, expected_type, return_type); - assert(!node->codegen_node); - node->codegen_node = allocate(1); - node->codegen_node->data.expr_node.type_entry = return_type; + if (node->codegen_node) { + assert(node->type == NodeTypeGoto); + } else { + assert(node->type != NodeTypeGoto); + node->codegen_node = allocate(1); + } + node->codegen_node->expr_node.type_entry = return_type; return return_type; } @@ -652,6 +708,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, case NodeTypeCastExpr: case NodeTypePrefixOpExpr: case NodeTypeIfExpr: + case NodeTypeLabel: + case NodeTypeGoto: zig_unreachable(); } } diff --git a/src/codegen.cpp b/src/codegen.cpp index 13d72f6129..e91464771a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -115,7 +115,7 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) { } static TypeTableEntry *get_expr_type(AstNode *node) { - return node->codegen_node->data.expr_node.type_entry; + return node->codegen_node->expr_node.type_entry; } static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { @@ -407,11 +407,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, then_block); LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block); - LLVMBuildBr(g->builder, endif_block); + if (get_expr_type(node->data.if_expr.then_block) != g->builtin_types.entry_unreachable) + LLVMBuildBr(g->builder, endif_block); LLVMPositionBuilderAtEnd(g->builder, else_block); LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node); - LLVMBuildBr(g->builder, endif_block); + if (get_expr_type(node->data.if_expr.else_node) != g->builtin_types.entry_unreachable) + LLVMBuildBr(g->builder, endif_block); LLVMPositionBuilderAtEnd(g->builder, endif_block); if (use_expr_value) { @@ -435,7 +437,8 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, then_block); gen_expr(g, node->data.if_expr.then_block); - LLVMBuildBr(g->builder, endif_block); + if (get_expr_type(node->data.if_expr.then_block) != g->builtin_types.entry_unreachable) + LLVMBuildBr(g->builder, endif_block); LLVMPositionBuilderAtEnd(g->builder, endif_block); return nullptr; @@ -518,6 +521,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { } case NodeTypeBlock: return gen_block(g, node, nullptr); + case NodeTypeGoto: + add_debug_source_node(g, node); + return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); + case NodeTypeLabel: + { + LLVMBasicBlockRef basic_block = node->codegen_node->data.label_entry->basic_block; + add_debug_source_node(g, node); + LLVMValueRef result = LLVMBuildBr(g->builder, basic_block); + LLVMPositionBuilderAtEnd(g->builder, basic_block); + return result; + } case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeFnProto: @@ -533,6 +547,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { zig_unreachable(); } +static void build_label_blocks(CodeGen *g, AstNode *block_node) { + assert(block_node->type == NodeTypeBlock); + for (int i = 0; i < block_node->data.block.statements.length; i += 1) { + AstNode *label_node = block_node->data.block.statements.at(i); + if (label_node->type != NodeTypeLabel) + continue; + + Buf *name = &label_node->data.label.name; + label_node->codegen_node->data.label_entry->basic_block = LLVMAppendBasicBlock( + g->cur_fn->fn_value, buf_ptr(name)); + } + +} + static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto, LLVMZigDIFile *di_file) { @@ -623,10 +651,13 @@ static void do_code_gen(CodeGen *g) { codegen_fn_def->params = allocate(LLVMCountParams(fn)); LLVMGetParams(fn, codegen_fn_def->params); + build_label_blocks(g, fn_def_node->data.fn_def.body); + TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type; gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type); g->block_scopes.pop(); + } assert(!g->errors.length); diff --git a/src/main.cpp b/src/main.cpp index f0060c2c1d..9c03aa2207 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,7 @@ #include "buffer.hpp" #include "codegen.hpp" #include "os.hpp" +#include "error.hpp" #include @@ -48,6 +49,8 @@ struct Build { }; static int build(const char *arg0, Build *b) { + int err; + if (!b->in_file) return usage(arg0); @@ -59,11 +62,17 @@ static int build(const char *arg0, Build *b) { Buf root_source_name = BUF_INIT; if (buf_eql_str(&in_file_buf, "-")) { os_get_cwd(&root_source_dir); - os_fetch_file(stdin, &root_source_code); + if ((err = os_fetch_file(stdin, &root_source_code))) { + fprintf(stderr, "unable to read stdin: %s\n", err_str(err)); + return 1; + } buf_init_from_str(&root_source_name, ""); } else { os_path_split(&in_file_buf, &root_source_dir, &root_source_name); - os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code); + if ((err = os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code))) { + fprintf(stderr, "unable to open '%s': %s\n", b->in_file, err_str(err)); + return 1; + } } CodeGen *g = codegen_create(&root_source_dir); diff --git a/src/parser.cpp b/src/parser.cpp index bd4c959608..24b7cc7790 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -95,6 +95,10 @@ const char *node_type_str(NodeType node_type) { return "Void"; case NodeTypeIfExpr: return "IfExpr"; + case NodeTypeLabel: + return "Label"; + case NodeTypeGoto: + return "Label"; } zig_unreachable(); } @@ -260,6 +264,12 @@ void ast_print(AstNode *node, int indent) { if (node->data.if_expr.else_node) ast_print(node->data.if_expr.else_node, indent + 2); break; + case NodeTypeLabel: + fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name)); + break; + case NodeTypeGoto: + fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name)); + break; } } @@ -581,7 +591,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool } /* -PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) +PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto */ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -609,6 +619,16 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool ast_buf_from_token(pc, token, &node->data.symbol); *token_index += 1; return node; + } else if (token->id == TokenIdKeywordGoto) { + AstNode *node = ast_create_node(pc, NodeTypeGoto, token); + *token_index += 1; + + Token *dest_symbol = &pc->tokens->at(*token_index); + *token_index += 1; + ast_expect_token(pc, dest_symbol, TokenIdSymbol); + + ast_buf_from_token(pc, dest_symbol, &node->data.go_to.name); + return node; } AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); @@ -1181,7 +1201,36 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma } /* -Statement : NonBlockExpression token(Semicolon) | BlockExpression +Label: token(Symbol) token(Colon) +*/ +static AstNode *ast_parse_label(ParseContext *pc, int *token_index, bool mandatory) { + Token *symbol_token = &pc->tokens->at(*token_index); + if (symbol_token->id != TokenIdSymbol) { + if (mandatory) { + ast_invalid_token_error(pc, symbol_token); + } else { + return nullptr; + } + } + + Token *colon_token = &pc->tokens->at(*token_index + 1); + if (colon_token->id != TokenIdColon) { + if (mandatory) { + ast_invalid_token_error(pc, colon_token); + } else { + return nullptr; + } + } + + *token_index += 2; + + AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token); + ast_buf_from_token(pc, symbol_token, &node->data.label.name); + return node; +} + +/* +Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) */ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) { @@ -1204,12 +1253,18 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato // {2;} -> {2;void} // {;2} -> {void;2} for (;;) { - AstNode *statement_node = ast_parse_block_expr(pc, token_index, false); - bool semicolon_expected = !statement_node; - if (!statement_node) { - statement_node = ast_parse_non_block_expr(pc, token_index, false); + AstNode *statement_node = ast_parse_label(pc, token_index, false); + bool semicolon_expected; + if (statement_node) { + semicolon_expected = false; + } else { + statement_node = ast_parse_block_expr(pc, token_index, false); + semicolon_expected = !statement_node; if (!statement_node) { - statement_node = ast_create_node(pc, NodeTypeVoid, last_token); + statement_node = ast_parse_non_block_expr(pc, token_index, false); + if (!statement_node) { + statement_node = ast_create_node(pc, NodeTypeVoid, last_token); + } } } node->data.block.statements.append(statement_node); diff --git a/src/parser.hpp b/src/parser.hpp index 8c7e86a20c..ed80988dbe 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -41,6 +41,8 @@ enum NodeType { NodeTypeUse, NodeTypeVoid, NodeTypeIfExpr, + NodeTypeLabel, + NodeTypeGoto, }; struct AstNodeRoot { @@ -182,6 +184,14 @@ struct AstNodeIfExpr { AstNode *else_node; // null, block node, or other if expr node }; +struct AstNodeLabel { + Buf name; +}; + +struct AstNodeGoto { + Buf name; +}; + struct AstNode { enum NodeType type; int line; @@ -207,6 +217,8 @@ struct AstNode { AstNodeFnCallExpr fn_call_expr; AstNodeUse use; AstNodeIfExpr if_expr; + AstNodeLabel label; + AstNodeGoto go_to; Buf number; Buf string; Buf symbol; diff --git a/src/semantic_info.hpp b/src/semantic_info.hpp index 2993948446..83002fdbb5 100644 --- a/src/semantic_info.hpp +++ b/src/semantic_info.hpp @@ -43,6 +43,11 @@ struct SymbolTableEntry { int param_index; // only valid in the case of parameters }; +struct LabelTableEntry { + AstNode *label_node; + LLVMBasicBlockRef basic_block; +}; + struct FnTableEntry { LLVMValueRef fn_value; AstNode *proto_node; @@ -54,6 +59,7 @@ struct FnTableEntry { // reminder: hash tables must be initialized before use HashMap symbol_table; + HashMap label_table; }; struct CodeGen { @@ -100,6 +106,7 @@ struct CodeGen { OutType out_type; FnTableEntry *cur_fn; + LLVMBasicBlockRef cur_basic_block; bool c_stdint_used; AstNode *root_export_decl; int version_major; @@ -132,9 +139,10 @@ struct CodeGenNode { union { TypeNode type_node; // for NodeTypeType FnDefNode fn_def_node; // for NodeTypeFnDef - ExprNode expr_node; // for all the expression nodes FnProtoNode fn_proto_node; // for NodeTypeFnProto + LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel } data; + ExprNode expr_node; // for all the expression nodes }; static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) { diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 82119ba28e..cae237f203 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -189,6 +189,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordIf; } else if (mem_eql_str(token_mem, token_len, "else")) { t->cur_tok->id = TokenIdKeywordElse; + } else if (mem_eql_str(token_mem, token_len, "goto")) { + t->cur_tok->id = TokenIdKeywordGoto; } t->cur_tok = nullptr; @@ -586,6 +588,7 @@ static const char * token_name(Token *token) { case TokenIdKeywordVoid: return "Void"; case TokenIdKeywordIf: return "If"; case TokenIdKeywordElse: return "Else"; + case TokenIdKeywordGoto: return "Goto"; case TokenIdLParen: return "LParen"; case TokenIdRParen: return "RParen"; case TokenIdComma: return "Comma"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 139b4727db..527f74e9eb 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -27,6 +27,7 @@ enum TokenId { TokenIdKeywordVoid, TokenIdKeywordIf, TokenIdKeywordElse, + TokenIdKeywordGoto, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 81104914ab..cfc4920116 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -255,6 +255,19 @@ void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder) { reinterpret_cast(dibuilder)->finalize(); } +LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder_wrapped) { + IRBuilderBase::InsertPoint *ip = new IRBuilderBase::InsertPoint(); + *ip = unwrap(builder_wrapped)->saveIP(); + return reinterpret_cast(ip); +} + +void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *ip_wrapped) { + IRBuilderBase::InsertPoint *ip = reinterpret_cast(ip_wrapped); + unwrap(builder)->restoreIP(*ip); +} + +//------------------------------------ + enum FloatAbi { FloatAbiHard, FloatAbiSoft, diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp index 4350624310..71bff360e8 100644 --- a/src/zig_llvm.hpp +++ b/src/zig_llvm.hpp @@ -22,6 +22,7 @@ struct LLVMZigDIFile; struct LLVMZigDILexicalBlock; struct LLVMZigDISubprogram; struct LLVMZigDISubroutineType; +struct LLVMZigInsertionPoint; void LLVMZigInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); void LLVMZigInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); @@ -75,6 +76,9 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder); +LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder); +void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *point); + /* * This stuff is not LLVM API but it depends on the LLVM C++ API so we put it here. diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 78f35db5a3..a8bc81bf11 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -232,6 +232,30 @@ static void add_compiling_test_cases(void) { exit(0); } )SOURCE", "pass\n"); + + add_simple_case("goto", R"SOURCE( + #link("c") + extern { + fn puts(s: *const u8) -> i32; + fn exit(code: i32) -> unreachable; + } + + fn loop(a : i32) { + if a == 0 { + goto done; + } + puts("loop"); + loop(a - 1); + + done: + return; + } + + export fn _start() -> unreachable { + loop(3); + exit(0); + } + )SOURCE", "loop\nloop\nloop\n"); } static void add_compile_failure_test_cases(void) { @@ -305,6 +329,17 @@ fn a() { )SOURCE", 2, ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'", ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'"); + + add_compile_fail_case("goto cause unreachable code", R"SOURCE( +fn a() { + goto done; + b(); +done: + return; +} +fn b() {} + )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code"); + } static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) {