diff --git a/src/all_types.hpp b/src/all_types.hpp index c17205ee43..aced310fc5 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1128,6 +1128,7 @@ enum FnInline { struct FnTableEntry { LLVMValueRef llvm_value; + const char *llvm_name; AstNode *proto_node; AstNode *body_node; ScopeFnDef *fndef_scope; // parent should be the top level decls or container decls @@ -1501,6 +1502,8 @@ struct CodeGen { Buf *cache_dir; Buf *out_h_path; + + ZigList inline_fns; }; enum VarLinkage { diff --git a/src/codegen.cpp b/src/codegen.cpp index 4d7398c7b5..6a5d2b1ef2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -353,10 +353,12 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { } else { fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); } + fn_table_entry->llvm_name = LLVMGetValueName(fn_table_entry->llvm_value); switch (fn_table_entry->fn_inline) { case FnInlineAlways: addLLVMFnAttr(fn_table_entry->llvm_value, "alwaysinline"); + g->inline_fns.append(fn_table_entry); break; case FnInlineNever: addLLVMFnAttr(fn_table_entry->llvm_value, "noinline"); @@ -3628,6 +3630,27 @@ static void ensure_cache_dir(CodeGen *g) { } } +static void report_errors_and_maybe_exit(CodeGen *g) { + if (g->errors.length != 0) { + for (size_t i = 0; i < g->errors.length; i += 1) { + ErrorMsg *err = g->errors.at(i); + print_err_msg(err, g->err_color); + } + exit(1); + } +} + +static void validate_inline_fns(CodeGen *g) { + for (size_t i = 0; i < g->inline_fns.length; i += 1) { + FnTableEntry *fn_entry = g->inline_fns.at(i); + LLVMValueRef fn_val = LLVMGetNamedFunction(g->module, fn_entry->llvm_name); + if (fn_val != nullptr) { + add_node_error(g, fn_entry->proto_node, buf_sprintf("unable to inline function")); + } + } + report_errors_and_maybe_exit(g); +} + static void do_code_gen(CodeGen *g) { if (g->verbose) { fprintf(stderr, "\nCode Generation:\n"); @@ -3927,6 +3950,8 @@ static void do_code_gen(CodeGen *g) { zig_panic("unable to write object file: %s", err_msg); } + validate_inline_fns(g); + g->link_objects.append(output_path); } @@ -4719,16 +4744,9 @@ static void gen_root_source(CodeGen *g) { } } - if (g->errors.length == 0) { - if (g->verbose) { - fprintf(stderr, "OK\n"); - } - } else { - for (size_t i = 0; i < g->errors.length; i += 1) { - ErrorMsg *err = g->errors.at(i); - print_err_msg(err, g->err_color); - } - exit(1); + report_errors_and_maybe_exit(g); + if (g->verbose) { + fprintf(stderr, "OK\n"); } } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f6ff365451..f021898763 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1684,4 +1684,29 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\fn bar() -> ?i32 { 1 } , ".tmp_source.zig:2:15: error: expected error union type, found '?i32'"); + + cases.add("inline fn calls itself indirectly", + \\export fn foo() { + \\ bar(); + \\} + \\inline fn bar() { + \\ baz(); + \\ quux(); + \\} + \\inline fn baz() { + \\ bar(); + \\ quux(); + \\} + \\extern fn quux(); + , + ".tmp_source.zig:4:8: error: unable to inline function"); + + cases.add("save reference to inline function", + \\export fn foo() { + \\ quux(usize(bar)); + \\} + \\inline fn bar() { } + \\extern fn quux(usize); + , + ".tmp_source.zig:4:8: error: unable to inline function"); }