Commit 866c841dd8

Andrew Kelley <superjoe30@gmail.com>
2017-05-04 21:00:25
add compile error when unable to inline a function
See #38
1 parent 5c094d7
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<FnTableEntry *> inline_fns;
 };
 
 enum VarLinkage {
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");
     }
 
 }
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");
 }