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 38a871d2f07971576d47d4b5670f3af16ff7ffa6..49f9135a507d7505122c9d1dd5510c631d607fd7 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 3804421849a353e62d03c003f95d553f652d5e6a..c8a4918cc6db347e2ae37897fd542ed965bc5fee 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 d950d9aa178df7fd7da7b8f2401036f45e4bf436..8742f90c4e5e43d19bb95dd98a877b2554031116 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 ef3b9ff760150a81edb6559edb4380e619ae5718..c7f31d937fea3a6ea7fc9c23a005f9c0d287a062 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 13d72f6129f9dc5ecc9087309bdf2d77b1ecebc9..e91464771a259cab53c93537ea0de4ab6b3a5a88 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 f0060c2c1d9ad4f1f1f41ffdc135285c77afafd6..9c03aa22079c1188326e4ee403bc043298781e4f 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 bd4c9596082ec1a90d5f94ed5dab139541120399..24b7cc7790fac2db83114204288ce82c4001d00b 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 8c7e86a20c80de884d1b7f7b9565a53e7b3eda7d..ed80988dbe3e1d88be95eab132de50945413fb35 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 2993948446daf0811a38114926ee137566c56a5c..83002fdbb50544db4988f3f00b2351087b6867b9 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 82119ba28efa9225c043189092852056c738e39a..cae237f203939d898b53930831a868c2a8c40561 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 139b4727db36c1d2dc3604cbba9cbc91d6c91d57..527f74e9eba47f6fd2d78a99754cf4c50b4b9953 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 81104914ab9262c43402ccb28809dc94244e78e0..cfc4920116c12841f4bffd5686543ba8e368b705 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 4350624310693e285375a5859c7ae9c8a4228b61..71bff360e8a403073b3a985f00520186d1774333 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 78f35db5a38505eb600ab7afcb1ae0f9b9d2cae3..a8bc81bf1167b800957b0a01e4e10313eb14082d 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) { -- 2.54.0