Merge pull request #7185 from subotac/fix/variadic-tuple-arguments

fix: pack tuple results into variadic arguments
This commit is contained in:
gingerBill
2026-08-05 16:56:24 +02:00
committed by GitHub
3 changed files with 122 additions and 71 deletions

View File

@@ -4904,6 +4904,79 @@ gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lb
}
}
gb_internal lbValue lb_build_variadic_slice(lbProcedure *p, Type *slice_type, Slice<lbValue> 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;
@@ -4989,8 +5062,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<lbValue>(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));
@@ -5032,82 +5142,13 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal
variadic_args = lb_build_expr(p, variadic[0]);
variadic_args = lb_emit_conv(p, variadic_args, slice_type);
} else {
Type *elem_type = slice_type->Slice.elem;
auto var_args = array_make<lbValue>(heap_allocator(), 0, variadic.count);
defer (array_free(&var_args));
for (Ast *var_arg : variadic) {
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);

View File

@@ -84,6 +84,7 @@ else
fi
$ODIN check ../test_issue_6979.odin -no-entry-point $COMMON_CHECK
$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"

View File

@@ -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({}))
}