c variadic calls

This commit is contained in:
2026-06-14 15:53:45 +02:00
parent 638ca57f5c
commit 2d3d0bd266
21 changed files with 409 additions and 35 deletions
+69 -9
View File
@@ -454,7 +454,7 @@ add_unsupported_type_diagnostic :: proc(
}
function_signatures_equal :: proc(left, right: ast.Function) -> bool {
if left.result != right.result || len(left.params) != len(right.params) {
if left.result != right.result || left.variadic != right.variadic || len(left.params) != len(right.params) {
return false
}
for param, index in left.params {
@@ -465,6 +465,17 @@ function_signatures_equal :: proc(left, right: ast.Function) -> bool {
return true
}
valid_call_arity :: proc(function: ast.Function, count: int) -> bool {
return count >= len(function.params) if function.variadic else count == len(function.params)
}
call_arg_expected :: proc(function: ast.Function, index: int) -> types.Type {
if index < 0 || index >= len(function.params) {
return types.INVALID
}
return type_from_syntax(function.params[index].type)
}
contains_name :: proc(names: []symbol.Id, name: symbol.Id) -> bool {
for existing in names {
if existing == name {
@@ -566,6 +577,14 @@ validate_declarations :: proc(checker: ^Checker) {
symbol_text(checker, function.name),
)
}
if function.variadic && (!function.c_abi || function.has_body) {
checker.template_diagnostics[function_id] = source.addf(
checker.diagnostics,
function.span,
"variadic function '%s' must be a bodyless 'c_func' declaration",
symbol_text(checker, function.name),
)
}
if !function.has_body && function.c_abi {
for param in function.params {
if add_unsupported_type_diagnostic(checker, param.span, type_from_syntax(param.type)) !=
@@ -1029,11 +1048,12 @@ infer_expr :: proc(
}
}
function := checker.ast_module.functions[frame.template]
if can_specialize(checker, function, stack[frame_index].args) {
if valid_call_arity(function, len(expr.args)) &&
can_specialize(checker, function, stack[frame_index].args) {
spec := INVALID_SPEC
if demanded == nil {
spec = ensure_spec(checker, frame.template, stack[frame_index].args)
} else if len(expr.args) == len(function.params) {
} else {
spec = find_spec(checker, frame.template, stack[frame_index].args)
mark_spec_demanded(checker, spec, demanded)
}
@@ -1317,6 +1337,32 @@ coerce_expr :: proc(
return invalid_hir_expr(checker, span, id, expected)
}
promote_c_vararg_expr :: proc(checker: ^Checker, expr_id: hir.Expr_Id, span: source.Span) -> hir.Expr_Id {
actual := checker.module.exprs[expr_id].type
if !types.is_c_vararg_type(actual, &checker.module.types) {
id := source.addf(
checker.diagnostics,
span,
"C variadic argument must be a concrete scalar or pointer, got %s",
types.name(actual),
)
return invalid_hir_expr(checker, span, id, types.C_INT)
}
promoted := types.c_vararg_promotion(actual, checker.target)
if types.equal(actual, promoted) {
return expr_id
}
return add_hir_expr(checker, hir.Expr{
kind=.C_Vararg_Promote,
span=span,
type=promoted,
left=expr_id,
target=hir.INVALID_REF,
right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
build_constant_expr :: proc(
checker: ^Checker,
expr: ast.Expr,
@@ -1891,13 +1937,16 @@ build_expr :: proc(
_ = pop(&stack)
continue
}
if len(expr.args) != len(checker.ast_module.functions[template].params) {
function := checker.ast_module.functions[template]
if !valid_call_arity(function, len(expr.args)) {
message := "function '%s' expects at least %d arguments, got %d" if function.variadic else
"function '%s' expects %d arguments, got %d"
id := source.addf(
checker.diagnostics,
expr.span,
"function '%s' expects %d arguments, got %d",
message,
symbol_text(checker, expr.name),
len(checker.ast_module.functions[template].params),
len(function.params),
len(expr.args),
)
last = invalid_hir_expr(checker, expr.span, id)
@@ -1909,7 +1958,7 @@ build_expr :: proc(
stack[frame_index].arg_types = make([]types.Type, len(expr.args), checker.allocator)
stack[frame_index].stage = 3
if len(expr.args) > 0 {
arg_expected := type_from_syntax(checker.ast_module.functions[template].params[0].type)
arg_expected := call_arg_expected(function, 0)
if !is_runtime_type(checker, arg_expected) {
arg_expected = types.INVALID
}
@@ -1980,7 +2029,7 @@ build_expr :: proc(
stack[frame_index].arg_index += 1
if frame.arg_index+1 < len(expr.args) {
next := frame.arg_index+1
arg_expected := type_from_syntax(checker.ast_module.functions[frame.template].params[next].type)
arg_expected := call_arg_expected(checker.ast_module.functions[frame.template], next)
if !is_runtime_type(checker, arg_expected) {
arg_expected = types.INVALID
}
@@ -2004,7 +2053,8 @@ build_expr :: proc(
_ = pop(&stack)
continue
}
for _, index in stack[frame_index].built_args {
fixed_count := len(checker.ast_module.functions[frame.template].params)
for index in 0..<fixed_count {
stack[frame_index].built_args[index] = coerce_expr(
checker,
stack[frame_index].built_args[index],
@@ -2012,6 +2062,14 @@ build_expr :: proc(
checker.module.exprs[stack[frame_index].built_args[index]].span,
)
}
for index in fixed_count..<len(stack[frame_index].built_args) {
arg := stack[frame_index].built_args[index]
stack[frame_index].built_args[index] = promote_c_vararg_expr(
checker,
arg,
checker.module.exprs[arg].span,
)
}
function_id := checker.specs[spec].hir_id
assert(function_id != hir.INVALID_FUNCTION)
add_unique_function(calls, function_id)
@@ -2128,6 +2186,7 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
implementation = .Declaration,
linkage = .External if function.c_abi else .Internal,
is_main = function.pkg == 0 && function.name == checker.main_symbol,
variadic = function.variadic,
params = params[:],
result = spec.result,
locals = hir_locals[:],
@@ -2524,6 +2583,7 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
implementation = .Definition,
linkage = .External if function.c_abi || (function.pkg == 0 && function.name == checker.main_symbol) else .Internal,
is_main = function.pkg == 0 && function.name == checker.main_symbol,
variadic = function.variadic,
params = params[:],
result = spec.result,
locals = hir_locals[:],