From fcdd808c5c1b866c2582a17839a53ce7bbbb78d6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 May 2017 13:48:10 -0400 Subject: [PATCH] fix segfault with array of variadic functions closes #377 --- src/all_types.hpp | 1 + src/analyze.cpp | 13 +++++++++++-- src/analyze.hpp | 1 + src/ir.cpp | 31 +++++++++++++++++++++++++++---- test/compile_errors.zig | 12 ++++++++++++ 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 292d3ebfc..89f10aade 100644 --- a/src/all_types.hpp +++ b/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 diff --git a/src/analyze.cpp b/src/analyze.cpp index 2f586f3a3..fbf2eab9c 100644 --- a/src/analyze.cpp +++ b/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]; diff --git a/src/analyze.hpp b/src/analyze.hpp index 6038e4b23..dd456482f 100644 --- a/src/analyze.hpp +++ b/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 diff --git a/src/ir.cpp b/src/ir.cpp index 3a22218ee..b44428191 100644 --- a/src/ir.cpp +++ b/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(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(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]; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f5a00d0d1..ab9654aee 100644 --- a/test/compile_errors.zig +++ b/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"); }