c variadic calls
This commit is contained in:
@@ -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[:],
|
||||
|
||||
Reference in New Issue
Block a user