Commit d784705353

Andrew Kelley <superjoe30@gmail.com>
2017-01-12 09:15:06
IR: implement macro for function aliasing function pointer
1 parent 76b1cbc
src/analyze.cpp
@@ -193,7 +193,7 @@ Scope *create_loop_scope(AstNode *node, Scope *parent) {
 }
 
 ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {
-    assert(node->type == NodeTypeFnDef);
+    assert(!node || node->type == NodeTypeFnDef);
     ScopeFnDef *scope = allocate<ScopeFnDef>(1);
     init_scope(&scope->base, ScopeIdFnDef, node, parent);
     scope->fn_entry = fn_entry;
@@ -2341,31 +2341,20 @@ bool type_is_codegen_pointer(TypeTableEntry *type) {
 AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) {
     if (fn_entry->param_source_nodes)
         return fn_entry->param_source_nodes[index];
-    else
+    else if (fn_entry->proto_node)
         return fn_entry->proto_node->data.fn_proto.params.at(index);
+    else
+        return nullptr;
 }
 
-static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
-    assert(fn_table_entry->anal_state != FnAnalStateProbing);
-    if (fn_table_entry->anal_state != FnAnalStateReady)
-        return;
-
-    fn_table_entry->anal_state = FnAnalStateProbing;
-
-    AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
-
-    assert(fn_table_entry->fndef_scope);
-    if (!fn_table_entry->child_scope)
-        fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
-
-    // define local variables for parameters
+void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) {
     TypeTableEntry *fn_type = fn_table_entry->type_entry;
     assert(!fn_type->data.fn.is_generic);
     FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
     for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
         FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
         AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
-        AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl;
+        Buf *param_name = param_decl_node ? param_decl_node->data.param_decl.name : buf_sprintf("arg%zu", i);
 
         TypeTableEntry *param_type = param_info->type;
         bool is_noalias = param_info->is_noalias;
@@ -2380,7 +2369,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
         }
 
         VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
-                param_decl->name, true, create_const_runtime(param_type));
+                param_name, true, create_const_runtime(param_type));
         var->src_arg_index = i;
         fn_table_entry->child_scope = var->child_scope;
 
@@ -2391,30 +2380,20 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
         if (fn_type->data.fn.gen_param_info) {
             var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
         }
-    }
-
-    TypeTableEntry *expected_type = fn_type_id->return_type;
 
-    if (fn_type_id->is_extern && handle_is_ptr(expected_type)) {
-        add_node_error(g, fn_proto->return_type,
-            buf_sprintf("byvalue types not yet supported on extern function return values"));
+        if (arg_vars) {
+            arg_vars[i] = var;
+        }
     }
+}
 
-    ir_gen_fn(g, fn_table_entry);
-    if (fn_table_entry->ir_executable.invalid) {
-        fn_table_entry->anal_state = FnAnalStateInvalid;
-        return;
-    }
-    if (g->verbose) {
-        fprintf(stderr, "\n");
-        ast_render(stderr, fn_table_entry->fn_def_node, 4);
-        fprintf(stderr, "\n{ // (IR)\n");
-        ir_print(stderr, &fn_table_entry->ir_executable, 4);
-        fprintf(stderr, "}\n");
-    }
+void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) {
+    TypeTableEntry *fn_type = fn_table_entry->type_entry;
+    assert(!fn_type->data.fn.is_generic);
+    FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
 
     TypeTableEntry *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable,
-            &fn_table_entry->analyzed_executable, expected_type, fn_proto->return_type);
+            &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);
     fn_table_entry->implicit_return_type = block_return_type;
 
     if (block_return_type->id == TypeTableEntryIdInvalid ||
@@ -2434,6 +2413,46 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
     fn_table_entry->anal_state = FnAnalStateComplete;
 }
 
+static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
+    assert(fn_table_entry->anal_state != FnAnalStateProbing);
+    if (fn_table_entry->anal_state != FnAnalStateReady)
+        return;
+
+    fn_table_entry->anal_state = FnAnalStateProbing;
+
+    AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type;
+
+    assert(fn_table_entry->fndef_scope);
+    if (!fn_table_entry->child_scope)
+        fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
+
+    define_local_param_variables(g, fn_table_entry, nullptr);
+
+    TypeTableEntry *fn_type = fn_table_entry->type_entry;
+    assert(!fn_type->data.fn.is_generic);
+    FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
+
+    if (fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type)) {
+        add_node_error(g, return_type_node,
+            buf_sprintf("byvalue types not yet supported on extern function return values"));
+    }
+
+    ir_gen_fn(g, fn_table_entry);
+    if (fn_table_entry->ir_executable.invalid) {
+        fn_table_entry->anal_state = FnAnalStateInvalid;
+        return;
+    }
+    if (g->verbose) {
+        fprintf(stderr, "\n");
+        ast_render(stderr, fn_table_entry->fn_def_node, 4);
+        fprintf(stderr, "\n{ // (IR)\n");
+        ir_print(stderr, &fn_table_entry->ir_executable, 4);
+        fprintf(stderr, "}\n");
+    }
+
+    analyze_fn_ir(g, fn_table_entry, return_type_node);
+}
+
 static void add_symbols_from_import(CodeGen *g, AstNode *dst_use_node) {
     IrInstruction *use_target_value = dst_use_node->data.use.value;
     if (use_target_value->value.type->id == TypeTableEntryIdInvalid) {
src/analyze.hpp
@@ -83,6 +83,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
 void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max);
 
 void render_const_value(Buf *buf, ConstExprValue *const_val);
+void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars);
+void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node);
 
 ScopeBlock *create_block_scope(AstNode *node, Scope *parent);
 ScopeDefer *create_defer_scope(AstNode *node, Scope *parent);
src/ast_render.cpp
@@ -908,7 +908,8 @@ static void ast_render_tld_fn(AstRender *ar, Buf *name, TldFn *tld_fn) {
         if (param_info->is_noalias) {
             fprintf(ar->f, "noalias ");
         }
-        fprintf(ar->f, "%s: %s", buf_ptr(tld_fn->fn_entry->param_names[i]), buf_ptr(&param_info->type->name));
+        Buf *param_name = tld_fn->fn_entry->param_names ? tld_fn->fn_entry->param_names[i] : buf_sprintf("arg%zu", i);
+        fprintf(ar->f, "%s: %s", buf_ptr(param_name), buf_ptr(&param_info->type->name));
     }
     if (fn_type_id->return_type->id == TypeTableEntryIdVoid) {
         fprintf(ar->f, ");\n");
@@ -947,24 +948,28 @@ static void ast_render_tld_var(AstRender *ar, Buf *name, TldVar *tld_var) {
         if (type_entry->id == TypeTableEntryIdStruct) {
             const char *extern_str = extern_string(type_entry->data.structure.is_extern);
             fprintf(ar->f, "%sstruct {\n", extern_str);
-            for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
-                TypeStructField *field = &type_entry->data.structure.fields[i];
-                fprintf(ar->f, "    ");
-                print_symbol(ar, field->name);
-                fprintf(ar->f, ": %s,\n", buf_ptr(&field->type_entry->name));
+            if (type_entry->data.structure.complete) {
+                for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
+                    TypeStructField *field = &type_entry->data.structure.fields[i];
+                    fprintf(ar->f, "    ");
+                    print_symbol(ar, field->name);
+                    fprintf(ar->f, ": %s,\n", buf_ptr(&field->type_entry->name));
+                }
             }
             fprintf(ar->f, "}");
         } else if (type_entry->id == TypeTableEntryIdEnum) {
             const char *extern_str = extern_string(type_entry->data.enumeration.is_extern);
             fprintf(ar->f, "%senum {\n", extern_str);
-            for (size_t i = 0; i < type_entry->data.enumeration.src_field_count; i += 1) {
-                TypeEnumField *field = &type_entry->data.enumeration.fields[i];
-                fprintf(ar->f, "    ");
-                print_symbol(ar, field->name);
-                if (field->type_entry->id == TypeTableEntryIdVoid) {
-                    fprintf(ar->f, ",\n");
-                } else {
-                    fprintf(ar->f, ": %s,\n", buf_ptr(&field->type_entry->name));
+            if (type_entry->data.enumeration.complete) {
+                for (size_t i = 0; i < type_entry->data.enumeration.src_field_count; i += 1) {
+                    TypeEnumField *field = &type_entry->data.enumeration.fields[i];
+                    fprintf(ar->f, "    ");
+                    print_symbol(ar, field->name);
+                    if (field->type_entry->id == TypeTableEntryIdVoid) {
+                        fprintf(ar->f, ",\n");
+                    } else {
+                        fprintf(ar->f, ": %s,\n", buf_ptr(&field->type_entry->name));
+                    }
                 }
             }
             fprintf(ar->f, "}");
src/codegen.cpp
@@ -287,6 +287,8 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
             assert(scope->parent);
             ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
             FnTableEntry *fn_table_entry = fn_scope->fn_entry;
+            if (!fn_table_entry->proto_node)
+                return get_di_scope(g, scope->parent);
             unsigned line_number = fn_table_entry->proto_node->line + 1;
             unsigned scope_line = line_number;
             bool is_definition = fn_table_entry->fn_def_node != nullptr;
@@ -2808,7 +2810,6 @@ static void do_code_gen(CodeGen *g) {
             continue;
 
         assert(var->decl_node);
-        assert(var->decl_node->type == NodeTypeVariableDeclaration);
 
         LLVMValueRef global_value;
         if (var->is_extern) {
@@ -3033,9 +3034,11 @@ static void do_code_gen(CodeGen *g) {
                     unsigned align_bytes = ZigLLVMGetPrefTypeAlignment(g->target_data_ref, var->value.type->type_ref);
                     LLVMSetAlignment(var->value_ref, align_bytes);
                 }
-                var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
-                        buf_ptr(&var->name), import->di_file, var->decl_node->line + 1,
-                        gen_type->di_type, !g->strip_debug_symbols, 0, var->gen_arg_index + 1);
+                if (var->decl_node) {
+                    var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
+                            buf_ptr(&var->name), import->di_file, var->decl_node->line + 1,
+                            gen_type->di_type, !g->strip_debug_symbols, 0, var->gen_arg_index + 1);
+                }
 
             }
         }
@@ -3062,7 +3065,9 @@ static void do_code_gen(CodeGen *g) {
                 LLVMBuildStore(g->builder, LLVMGetParam(fn, variable->gen_arg_index), variable->value_ref);
             }
 
-            gen_var_debug_decl(g, variable);
+            if (variable->decl_node) {
+                gen_var_debug_decl(g, variable);
+            }
         }
 
         ir_render(g, fn_table_entry);
src/ir.cpp
@@ -500,7 +500,6 @@ static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_no
 
 template<typename T>
 static T *ir_build_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
-    assert(source_node);
     T *special_instruction = ir_create_instruction<T>(irb, scope, source_node);
     ir_instruction_append(irb->current_basic_block, &special_instruction->base);
     return special_instruction;
@@ -10120,7 +10119,7 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc
     find_libc_include_path(ira->codegen);
 
     ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
-    child_import->decls_scope = create_decls_scope(child_import->root, nullptr, nullptr, child_import);
+    child_import->decls_scope = create_decls_scope(node, nullptr, nullptr, child_import);
     child_import->c_import_node = node;
 
     ZigList<ErrorMsg *> errors = {0};
@@ -10143,7 +10142,7 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc
     if (ira->codegen->verbose) {
         fprintf(stderr, "\nC imports:\n");
         fprintf(stderr, "-----------\n");
-        ir_print_decls(stderr, child_import);
+        ast_render_decls(stderr, 4, child_import);
     }
 
     // TODO to get fewer false negatives on this, we would need to track this value in
@@ -11546,3 +11545,64 @@ bool ir_has_side_effects(IrInstruction *instruction) {
     zig_unreachable();
 }
 
+FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableEntry *var, Scope *parent_scope) {
+    FnTableEntry *fn_entry = create_fn_raw(FnInlineAuto, true);
+    buf_init_from_buf(&fn_entry->symbol_name, fn_name);
+
+    fn_entry->fndef_scope = create_fndef_scope(nullptr, parent_scope, fn_entry);
+    fn_entry->child_scope = &fn_entry->fndef_scope->base;
+
+    assert(var->value.type->id == TypeTableEntryIdMaybe);
+    TypeTableEntry *src_fn_type = var->value.type->data.maybe.child_type;
+    assert(src_fn_type->id == TypeTableEntryIdFn);
+
+    FnTypeId new_fn_type = src_fn_type->data.fn.fn_type_id;
+    new_fn_type.is_extern = false;
+
+    fn_entry->type_entry = get_fn_type(codegen, &new_fn_type);
+
+    IrBuilder ir_builder = {0};
+    IrBuilder *irb = &ir_builder;
+
+    irb->codegen = codegen;
+    irb->exec = &fn_entry->ir_executable;
+
+    AstNode *source_node = parent_scope->source_node;
+
+    size_t arg_count = fn_entry->type_entry->data.fn.fn_type_id.param_count;
+    IrInstruction **args = allocate<IrInstruction *>(arg_count);
+    VariableTableEntry **arg_vars = allocate<VariableTableEntry *>(arg_count);
+
+    define_local_param_variables(codegen, fn_entry, arg_vars);
+    Scope *scope = fn_entry->child_scope;
+
+    irb->current_basic_block = ir_build_basic_block(irb, scope, "Entry");
+    // Entry block gets a reference because we enter it to begin.
+    ir_ref_bb(irb->current_basic_block);
+
+    IrInstruction *maybe_fn_ptr = ir_build_var_ptr(irb, scope, source_node, var, true);
+    IrInstruction *unwrapped_fn_ptr = ir_build_unwrap_maybe(irb, scope, source_node, maybe_fn_ptr, true);
+    IrInstruction *fn_ref_instruction = ir_build_load_ptr(irb, scope, source_node, unwrapped_fn_ptr);
+
+    for (size_t i = 0; i < arg_count; i += 1) {
+        IrInstruction *var_ptr_instruction = ir_build_var_ptr(irb, scope, source_node, arg_vars[i], true);
+        args[i] = ir_build_load_ptr(irb, scope, source_node, var_ptr_instruction);
+    }
+
+    IrInstruction *call_instruction = ir_build_call(irb, scope, source_node, nullptr, fn_ref_instruction,
+            arg_count, args, false);
+    ir_build_return(irb, scope, source_node, call_instruction);
+
+    if (codegen->verbose) {
+        fprintf(stderr, "{\n");
+        ir_print(stderr, &fn_entry->ir_executable, 4);
+        fprintf(stderr, "}\n");
+    }
+
+    analyze_fn_ir(codegen, fn_entry, nullptr);
+
+    codegen->fn_defs.append(fn_entry);
+
+    return fn_entry;
+}
+
src/ir.hpp
@@ -24,4 +24,6 @@ TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutabl
 bool ir_has_side_effects(IrInstruction *instruction);
 ConstExprValue *const_ptr_pointee(ConstExprValue *const_val);
 
+FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableEntry *var, Scope *parent_scope);
+
 #endif
src/ir_print.cpp
@@ -1093,67 +1093,3 @@ void ir_print(FILE *f, IrExecutable *executable, int indent_size) {
         }
     }
 }
-
-static void print_tld_var(IrPrint *irp, TldVar *tld_var) {
-    const char *const_or_var = tld_var->var->src_is_const ? "const" : "var";
-    fprintf(irp->f, "%s %s", const_or_var, buf_ptr(tld_var->base.name));
-    bool omit_type = (tld_var->var->value.type->id == TypeTableEntryIdNumLitFloat ||
-        tld_var->var->value.type->id == TypeTableEntryIdNumLitInt);
-    if (!omit_type) {
-        fprintf(irp->f, ": %s", buf_ptr(&tld_var->var->value.type->name));
-    }
-    if (tld_var->var->value.special != ConstValSpecialRuntime) {
-        fprintf(irp->f, " = ");
-        ir_print_const_value(irp, &tld_var->var->value);
-    }
-    fprintf(irp->f, ";\n");
-}
-
-static void print_tld_fn(IrPrint *irp, TldFn *tld_fn) {
-    fprintf(irp->f, "// %s = TODO (function)\n", buf_ptr(tld_fn->base.name));
-}
-
-static void print_tld_container(IrPrint *irp, TldContainer *tld_container) {
-    fprintf(irp->f, "// %s = TODO (container)\n", buf_ptr(tld_container->base.name));
-}
-
-static void print_tld_typedef(IrPrint *irp, TldTypeDef *tld_typedef) {
-    fprintf(irp->f, "// %s = TODO (typedef)\n", buf_ptr(tld_typedef->base.name));
-}
-
-void ir_print_decls(FILE *f, ImportTableEntry *import) {
-    IrPrint ir_print = {};
-    IrPrint *irp = &ir_print;
-    irp->f = f;
-    irp->indent = 0;
-    irp->indent_size = 2;
-
-    auto it = import->decls_scope->decl_table.entry_iterator();
-    for (;;) {
-        auto *entry = it.next();
-        if (!entry)
-            break;
-
-        Tld *tld = entry->value;
-        if (!buf_eql_buf(entry->key, tld->name)) {
-            fprintf(f, "// alias: %s = %s\n", buf_ptr(entry->key), buf_ptr(tld->name));
-            continue;
-        }
-
-        switch (tld->id) {
-            case TldIdVar:
-                print_tld_var(irp, (TldVar *)tld);
-                continue;
-            case TldIdFn:
-                print_tld_fn(irp, (TldFn *)tld);
-                continue;
-            case TldIdContainer:
-                print_tld_container(irp, (TldContainer *)tld);
-                continue;
-            case TldIdTypeDef:
-                print_tld_typedef(irp, (TldTypeDef *)tld);
-                continue;
-        }
-        zig_unreachable();
-    }
-}
src/ir_print.hpp
@@ -14,7 +14,4 @@
 
 void ir_print(FILE *f, IrExecutable *executable, int indent_size);
 
-void ir_print_decls(FILE *f, ImportTableEntry *import);
-
-
 #endif
src/parseh.cpp
@@ -5,14 +5,15 @@
  * See http://opensource.org/licenses/MIT
  */
 
-#include "parseh.hpp"
+#include "all_types.hpp"
+#include "analyze.hpp"
+#include "c_tokenizer.hpp"
 #include "config.h"
-#include "os.hpp"
 #include "error.hpp"
+#include "ir.hpp"
+#include "os.hpp"
+#include "parseh.hpp"
 #include "parser.hpp"
-#include "all_types.hpp"
-#include "c_tokenizer.hpp"
-#include "analyze.hpp"
 
 #include <clang/Frontend/ASTUnit.h>
 #include <clang/Frontend/CompilerInstance.h>
@@ -133,6 +134,13 @@ static void parseh_init_tld(Context *c, Tld *tld, TldId id, Buf *name) {
     tld->resolution = TldResolutionOk;
 }
 
+static Tld *create_inline_fn_tld(Context *c, Buf *fn_name, TldVar *tld_var) {
+    TldFn *tld_fn = allocate<TldFn>(1);
+    parseh_init_tld(c, &tld_fn->base, TldIdFn, fn_name);
+    tld_fn->fn_entry = ir_create_inline_fn(c->codegen, fn_name, tld_var->var, &c->import->decls_scope->base);
+    return &tld_fn->base;
+}
+
 static TldVar *create_global_var(Context *c, Buf *name, ConstExprValue *var_value, bool is_const) {
     auto entry = c->import->decls_scope->decl_table.maybe_get(name);
     if (entry) {
@@ -143,6 +151,7 @@ static TldVar *create_global_var(Context *c, Buf *name, ConstExprValue *var_valu
     TldVar *tld_var = allocate<TldVar>(1);
     parseh_init_tld(c, &tld_var->base, TldIdVar, name);
     tld_var->var = add_variable(c->codegen, c->source_node, &c->import->decls_scope->base, name, is_const, var_value);
+    c->codegen->global_vars.append(tld_var->var);
     return tld_var;
 }
 
@@ -1229,9 +1238,8 @@ static void process_symbol_macros(Context *c) {
             if (var_type->id == TypeTableEntryIdMaybe && !tld_var->var->src_is_const) {
                 TypeTableEntry *child_type = var_type->data.maybe.child_type;
                 if (child_type->id == TypeTableEntryIdFn) {
-                    zig_panic("TODO macro alias of function pointer in .h file");
-                    //Tld *fn_tld = create_inline_fn_alias(c, ms.name, tld_var->var);
-                    //c->macro_table.put(ms.name, fn_tld);
+                    Tld *tld = create_inline_fn_tld(c, ms.name, tld_var);
+                    c->macro_table.put(ms.name, tld);
                     continue;
                 }
             }
test/run_tests.cpp
@@ -202,7 +202,7 @@ static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warn
 
     test_case->compiler_args.append("parseh");
     test_case->compiler_args.append(tmp_h_path);
-    test_case->compiler_args.append("--verbose");
+    //test_case->compiler_args.append("--verbose");
 
     test_cases.append(test_case);
 
@@ -1883,11 +1883,14 @@ Foo fun(Foo *a);
         R"SOURCE(
 extern void (*fn_ptr)(void);
 #define foo fn_ptr
-    )SOURCE", 2,
+
+extern char (*fn_ptr2)(int, float);
+#define bar fn_ptr2
+    )SOURCE", 4,
             "pub extern var fn_ptr: ?extern fn();",
-            R"SOURCE(pub inline fn foo() {
-    (??fn_ptr)();
-})SOURCE");
+            "pub fn foo();",
+            "pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8;",
+            "pub fn bar(arg0: c_int, arg1: f32) -> u8;");
 
 
     add_parseh_case("#define string", AllowWarningsNo, R"SOURCE(