Commit fcdd808c5c

Andrew Kelley <superjoe30@gmail.com>
2017-05-25 19:48:10
fix segfault with array of variadic functions
closes #377
1 parent 68add5d
src/all_types.hpp
@@ -2399,6 +2399,7 @@ struct IrInstructionFnProto {
 
     IrInstruction **param_types;
     IrInstruction *return_type;
+    bool is_var_args;
 };
 
 // true if the target value is compile time known, false otherwise
src/analyze.cpp
@@ -987,7 +987,7 @@ TypeTableEntry *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
     return result->value.data.x_type;
 }
 
-static TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
+TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
     fn_type->is_copyable = false;
     buf_init_from_str(&fn_type->name, "fn(");
@@ -2504,7 +2504,11 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *
         if (expected_type->data.fn.fn_type_id.is_cold != actual_type->data.fn.fn_type_id.is_cold) {
             return false;
         }
-        if (actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable &&
+        if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
+            return false;
+        }
+        if (!expected_type->data.fn.fn_type_id.is_var_args && 
+            actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable &&
             !types_match_const_cast_only(
                 expected_type->data.fn.fn_type_id.return_type,
                 actual_type->data.fn.fn_type_id.return_type))
@@ -2515,6 +2519,11 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *
             return false;
         }
         for (size_t i = 0; i < expected_type->data.fn.fn_type_id.param_count; i += 1) {
+            if (i == expected_type->data.fn.fn_type_id.param_count - 1 &&
+                expected_type->data.fn.fn_type_id.is_var_args)
+            {
+                continue;
+            }
             // note it's reversed for parameters
             FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
             FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i];
src/analyze.hpp
@@ -164,5 +164,6 @@ const char *type_id_name(TypeTableEntryId id);
 TypeTableEntryId type_id_at_index(size_t index);
 size_t type_id_len();
 size_t type_id_index(TypeTableEntryId id);
+TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
 
 #endif
src/ir.cpp
@@ -1857,14 +1857,17 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc
 }
 
 static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
-    IrInstruction **param_types, IrInstruction *return_type)
+    IrInstruction **param_types, IrInstruction *return_type, bool is_var_args)
 {
     IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
     instruction->param_types = param_types;
     instruction->return_type = return_type;
+    instruction->is_var_args = is_var_args;
 
     assert(source_node->type == NodeTypeFnProto);
-    for (size_t i = 0; i < source_node->data.fn_proto.params.length; i += 1) {
+    size_t param_count = source_node->data.fn_proto.params.length;
+    if (is_var_args) param_count -= 1;
+    for (size_t i = 0; i < param_count; i += 1) {
         ir_ref_instruction(param_types[i], irb->current_basic_block);
     }
     ir_ref_instruction(return_type, irb->current_basic_block);
@@ -5843,8 +5846,13 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
     size_t param_count = node->data.fn_proto.params.length;
     IrInstruction **param_types = allocate<IrInstruction*>(param_count);
 
+    bool is_var_args = false;
     for (size_t i = 0; i < param_count; i += 1) {
         AstNode *param_node = node->data.fn_proto.params.at(i);
+        if (param_node->data.param_decl.is_var_args) {
+            is_var_args = true;
+            break;
+        }
         AstNode *type_node = param_node->data.param_decl.type;
         IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope);
         if (type_value == irb->codegen->invalid_instruction)
@@ -5856,7 +5864,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
     if (return_type == irb->codegen->invalid_instruction)
         return irb->codegen->invalid_instruction;
 
-    return ir_build_fn_proto(irb, parent_scope, node, param_types, return_type);
+    return ir_build_fn_proto(irb, parent_scope, node, param_types, return_type, is_var_args);
 }
 
 static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
@@ -9039,7 +9047,11 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
     }
 
     if (fn_type->data.fn.is_generic) {
-        assert(fn_entry);
+        if (!fn_entry) {
+            ir_add_error(ira, call_instruction->fn_ref,
+                buf_sprintf("calling a generic function requires compile-time known function value"));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
 
         // Count the arguments of the function type id we are creating
         size_t new_fn_arg_count = first_arg_1_or_0;
@@ -13065,6 +13077,17 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
         AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
         assert(param_node->type == NodeTypeParamDecl);
 
+        bool param_is_var_args = param_node->data.param_decl.is_var_args;
+        if (param_is_var_args) {
+            if (fn_type_id.is_extern) {
+                fn_type_id.param_count = fn_type_id.next_param_index;
+                continue;
+            } else {
+                ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+                out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id);
+                return ira->codegen->builtin_types.entry_type;
+            }
+        }
         IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other;
 
         FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
test/compile_errors.zig
@@ -1892,4 +1892,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
         \\}
     ,
         ".tmp_source.zig:3:9: error: cannot goto out of defer expression");
+
+    cases.add("calling a var args function only known at runtime",
+        \\var foos = []fn(...) { foo1, foo2 };
+        \\
+        \\fn foo1(args: ...) {}
+        \\fn foo2(args: ...) {}
+        \\
+        \\pub fn main() -> %void {
+        \\    foos[0]();
+        \\}
+    ,
+        ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value");
 }