diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 9f1b5dd3f..3b5699160 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -4788,6 +4788,79 @@ gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lb } } +gb_internal lbValue lb_build_variadic_slice(lbProcedure *p, Type *slice_type, Slice var_args) { + GB_ASSERT(is_type_slice(slice_type)); + if (var_args.count == 0) { + return lb_const_nil(p->module, slice_type); + } + + Type *elem_type = slice_type->Slice.elem; + lbAddr slice = {}; + + for (auto const &vr : p->variadic_reuses) { + if (are_types_identical(vr.slice_type, slice_type)) { + slice = vr.slice_addr; + break; + } + } + + DeclInfo *d = decl_info_of_entity(p->entity); + if (d != nullptr && slice.addr.value == nullptr) { + for (auto const &vr : d->variadic_reuses) { + if (are_types_identical(vr.slice_type, slice_type)) { + #if LLVM_VERSION_MAJOR >= 13 + // NOTE(bill): No point wasting even more memory, just reuse this stack variable too + if (p->variadic_reuses.count > 0) { + slice = p->variadic_reuses[0].slice_addr; + } else { + slice = lb_add_local_generated(p, slice_type, true); + } + // NOTE(bill): Change the underlying type to match the specific type + slice.addr.type = alloc_type_pointer(slice_type); + #else + slice = lb_add_local_generated(p, slice_type, true); + #endif + array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice}); + break; + } + } + } + + lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + if (base_array_ptr.value == nullptr) { + if (d != nullptr) { + i64 max_bytes = d->variadic_reuse_max_bytes; + i64 max_align = gb_max(d->variadic_reuse_max_align, 16); + p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true); + lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align); + base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + } else { + base_array_ptr = lb_add_local_generated(p, alloc_type_array(elem_type, var_args.count), true).addr; + } + } + + if (slice.addr.value == nullptr) { + slice = lb_add_local_generated(p, slice_type, true); + } + + GB_ASSERT(base_array_ptr.value != nullptr); + GB_ASSERT(slice.addr.value != nullptr); + + base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, var_args.count))); + + for_array(i, var_args) { + lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i); + lbValue var_arg = lb_emit_conv(p, var_args[i], elem_type); + lb_emit_store(p, addr, var_arg); + } + + lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0); + lbValue len = lb_const_int(p->module, t_int, var_args.count); + lb_fill_slice(p, slice, base_elem, len); + + return lb_addr_load(p, slice); +} + gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbValue *sret_dst) { lbModule *m = p->module; @@ -4873,8 +4946,45 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal bool vari_expand = (ce->ellipsis.pos.line != 0); bool is_c_vararg = pt->c_vararg; + bool has_tuple_positional_arg = false; + if (pt->variadic && !is_c_vararg && !vari_expand) { + for (Ast *arg : ce->split_args->positional) { + TypeAndValue tav = type_and_value_of_expr(arg); + if (is_type_tuple(tav.type)) { + has_tuple_positional_arg = true; + break; + } + } + } - for_array(i, ce->split_args->positional) { + if (has_tuple_positional_arg) { + auto flat_args = array_make(heap_allocator()); + defer (array_free(&flat_args)); + + for_array(i, ce->split_args->positional) { + Entity *e = pt->params->Tuple.variables[gb_min(i, cast(isize)pt->variadic_index)]; + if (e->kind == Entity_TypeName) { + array_add(&flat_args, lb_const_nil(p->module, e->type)); + } else if (e->kind == Entity_Constant) { + array_add(&flat_args, lb_const_value(p->module, e->type, e->Constant.value)); + } else { + GB_ASSERT(e->kind == Entity_Variable); + lbValue arg = lb_build_expr(p, ce->split_args->positional[i]); + lb_add_values_to_array(p, &flat_args, arg); + } + } + + isize fixed_count = pt->variadic_index; + isize supplied_fixed_count = gb_min(fixed_count, flat_args.count); + for (isize i = 0; i < supplied_fixed_count; i++) { + array_add(&args, flat_args[i]); + } + array_resize(&args, fixed_count); + + Type *slice_type = pt->params->Tuple.variables[pt->variadic_index]->type; + auto var_args = slice(slice_from_array(flat_args), supplied_fixed_count, flat_args.count); + array_add(&args, lb_build_variadic_slice(p, slice_type, var_args)); + } else for_array(i, ce->split_args->positional) { Entity *e = pt->params->Tuple.variables[i]; if (e->kind == Entity_TypeName) { array_add(&args, lb_const_nil(p->module, e->type)); @@ -4924,74 +5034,7 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal lbValue v = lb_build_expr(p, var_arg); lb_add_values_to_array(p, &var_args, v); } - isize slice_len = var_args.count; - if (slice_len > 0) { - lbAddr slice = {}; - - for (auto const &vr : p->variadic_reuses) { - if (are_types_identical(vr.slice_type, slice_type)) { - slice = vr.slice_addr; - break; - } - } - - DeclInfo *d = decl_info_of_entity(p->entity); - if (d != nullptr && slice.addr.value == nullptr) { - for (auto const &vr : d->variadic_reuses) { - if (are_types_identical(vr.slice_type, slice_type)) { - #if LLVM_VERSION_MAJOR >= 13 - // NOTE(bill): No point wasting even more memory, just reuse this stack variable too - if (p->variadic_reuses.count > 0) { - slice = p->variadic_reuses[0].slice_addr; - } else { - slice = lb_add_local_generated(p, slice_type, true); - } - // NOTE(bill): Change the underlying type to match the specific type - slice.addr.type = alloc_type_pointer(slice_type); - #else - slice = lb_add_local_generated(p, slice_type, true); - #endif - array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice}); - break; - } - } - } - - lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr; - if (base_array_ptr.value == nullptr) { - if (d != nullptr) { - i64 max_bytes = d->variadic_reuse_max_bytes; - i64 max_align = gb_max(d->variadic_reuse_max_align, 16); - p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true); - lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align); - base_array_ptr = p->variadic_reuse_base_array_ptr.addr; - } else { - base_array_ptr = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true).addr; - } - } - - if (slice.addr.value == nullptr) { - slice = lb_add_local_generated(p, slice_type, true); - } - - GB_ASSERT(base_array_ptr.value != nullptr); - GB_ASSERT(slice.addr.value != nullptr); - - base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, slice_len))); - - for (isize i = 0; i < var_args.count; i++) { - lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i); - lbValue var_arg = var_args[i]; - var_arg = lb_emit_conv(p, var_arg, elem_type); - lb_emit_store(p, addr, var_arg); - } - - lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0); - lbValue len = lb_const_int(p->module, t_int, slice_len); - lb_fill_slice(p, slice, base_elem, len); - - variadic_args = lb_addr_load(p, slice); - } + variadic_args = lb_build_variadic_slice(p, slice_type, slice_from_array(var_args)); } } array_add(&args, variadic_args); diff --git a/tests/issues/run.sh b/tests/issues/run.sh index ce97a570a..a8134d865 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -82,6 +82,7 @@ else fi $ODIN check ../test_issue_6979.odin -no-entry-point $COMMON $ODIN build ../test_issue_7037.odin $COMMON -o:none +$ODIN build ../test_issue_7167.odin $COMMON if [[ $($ODIN build ../test_issue_7108.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then echo "SUCCESSFUL 1/1" diff --git a/tests/issues/test_issue_7167.odin b/tests/issues/test_issue_7167.odin new file mode 100644 index 000000000..6c4171088 --- /dev/null +++ b/tests/issues/test_issue_7167.odin @@ -0,0 +1,9 @@ +// Tests issue #7167 https://github.com/odin-lang/Odin/issues/7167 +package test_issues + +import "core:fmt" +import "core:path/filepath" + +main :: proc() { + fmt.printf(filepath.join({})) +}