function pointers and callbacks
This commit is contained in:
+436
-29
@@ -489,6 +489,71 @@ call_arg_expected :: proc(function: ast.Function, index: int) -> types.Type {
|
||||
return type_from_syntax(function.params[index].type)
|
||||
}
|
||||
|
||||
callable_arg_expected :: proc(function_type: types.Type, function_item: types.Node, store: ^types.Store, index: int) -> types.Type {
|
||||
if index < 0 || index >= int(function_item.field_count) {
|
||||
return types.INVALID
|
||||
}
|
||||
params := types.params_for(store, function_type)
|
||||
if index >= len(params) {
|
||||
return types.INVALID
|
||||
}
|
||||
return params[index].type
|
||||
}
|
||||
|
||||
valid_callable_arity :: proc(function_item: types.Node, count: int) -> bool {
|
||||
return count >= int(function_item.field_count) if function_item.variadic else count == int(function_item.field_count)
|
||||
}
|
||||
|
||||
function_value_signature :: proc(
|
||||
checker: ^Checker,
|
||||
template: ast.Function_Id,
|
||||
) -> (params: []types.Type, result: types.Type, ok: bool) {
|
||||
if template == ast.INVALID_FUNCTION || int(template) >= len(checker.ast_module.functions) {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
function := checker.ast_module.functions[template]
|
||||
if !function.c_abi {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
result = type_from_syntax(function.result)
|
||||
if !types.is_void(result) && !is_runtime_type(checker, result) {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
params = make([]types.Type, len(function.params), checker.allocator)
|
||||
for param, index in function.params {
|
||||
param_type := type_from_syntax(param.type)
|
||||
if !is_runtime_type(checker, param_type) {
|
||||
delete(params, checker.allocator)
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
params[index] = param_type
|
||||
}
|
||||
return params, result, true
|
||||
}
|
||||
|
||||
function_pointer_type_for_template :: proc(
|
||||
checker: ^Checker,
|
||||
template: ast.Function_Id,
|
||||
demanded: ^[dynamic]Spec_Id = nil,
|
||||
) -> (types.Type, Spec_Id, bool) {
|
||||
params, result, ok := function_value_signature(checker, template)
|
||||
if !ok {
|
||||
return types.INVALID, INVALID_SPEC, false
|
||||
}
|
||||
defer delete(params, checker.allocator)
|
||||
function := checker.ast_module.functions[template]
|
||||
function_type := types.function(&checker.module.types, params, result, true, function.variadic)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, true)
|
||||
spec := INVALID_SPEC
|
||||
if demanded == nil {
|
||||
spec = ensure_spec(checker, template, params)
|
||||
} else {
|
||||
spec = find_spec(checker, template, params)
|
||||
mark_spec_demanded(checker, spec, demanded)
|
||||
}
|
||||
return pointer_type, spec, spec != INVALID_SPEC
|
||||
}
|
||||
|
||||
contains_name :: proc(names: []symbol.Id, name: symbol.Id) -> bool {
|
||||
for existing in names {
|
||||
if existing == name {
|
||||
@@ -518,6 +583,9 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
||||
switch expr.kind {
|
||||
case .Call:
|
||||
append(&stack, ..expr.args)
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
append(&stack, expr.left)
|
||||
}
|
||||
case .Array, .Struct_Literal, .Slice:
|
||||
append(&stack, ..expr.args)
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
@@ -600,11 +668,15 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
}
|
||||
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)) !=
|
||||
param_type := type_from_syntax(param.type)
|
||||
if add_unsupported_type_diagnostic(checker, param.span, param_type) !=
|
||||
source.INVALID_DIAGNOSTIC {
|
||||
continue
|
||||
}
|
||||
if !types.is_c_signature_type(type_from_syntax(param.type), &checker.module.types) {
|
||||
if types.contains_c_struct_by_value(param_type, &checker.module.types) {
|
||||
continue
|
||||
}
|
||||
if !types.is_c_signature_type(param_type, &checker.module.types) {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
param.span,
|
||||
@@ -615,6 +687,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
}
|
||||
result := type_from_syntax(function.result)
|
||||
if add_unsupported_type_diagnostic(checker, function.span, result) == source.INVALID_DIAGNOSTIC &&
|
||||
!types.contains_c_struct_by_value(result, &checker.module.types) &&
|
||||
!types.is_c_signature_type(result, &checker.module.types, true) {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
@@ -683,17 +756,43 @@ validate_type_nodes :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if item.kind == .Struct {
|
||||
if item.kind == .Struct || item.kind == .Union {
|
||||
if item.c_layout && !item.opaque && item.field_count == 0 {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"c_struct definitions require at least one field",
|
||||
)
|
||||
}
|
||||
for field in types.fields_for(&checker.module.types, id) {
|
||||
if types.contains_c_struct_by_value(field.type, &checker.module.types) {
|
||||
if !types.is_runtime_value(field.type, &checker.module.types) {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"C records may only appear behind pointers",
|
||||
"record fields must have runtime value types",
|
||||
)
|
||||
} else if item.c_layout && !types.is_c_record_field_type(field.type, &checker.module.types) {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"c_struct fields must have C-layout-compatible types",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if item.kind == .Function {
|
||||
if !item.c_abi {
|
||||
source.add(checker.diagnostics, source.Span{}, "only c_func function pointer types are supported")
|
||||
}
|
||||
for param in types.params_for(&checker.module.types, id) {
|
||||
if types.is_void(param.type) || !types.is_c_signature_type(param.type, &checker.module.types) {
|
||||
source.add(checker.diagnostics, source.Span{}, "function pointer parameters must be concrete C signature types")
|
||||
}
|
||||
}
|
||||
if !types.is_c_signature_type(item.child, &checker.module.types, true) {
|
||||
source.add(checker.diagnostics, source.Span{}, "function pointer results must be concrete C signature types or void")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,7 +992,8 @@ infer_compound_expr :: proc(
|
||||
_ = infer_nested_expr(checker, checker.ast_module.exprs[keyed].left, locals, pkg, file, demanded)
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
return types.find_named(store, u32(target_pkg), u32(expr.name)) if available else types.INVALID
|
||||
value := types.find_named(store, u32(target_pkg), u32(expr.name)) if available else types.INVALID
|
||||
return types.resolve_alias(value, store)
|
||||
case .Keyed:
|
||||
return infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
case:
|
||||
@@ -994,6 +1094,20 @@ infer_expr :: proc(
|
||||
}
|
||||
}
|
||||
}
|
||||
if !types.is_valid(last) {
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
if available {
|
||||
template := find_template(checker, expr.name, target_pkg)
|
||||
if template != ast.INVALID_FUNCTION &&
|
||||
len(checker.ast_module.functions[template].unsupported_reason) == 0 &&
|
||||
checker.template_diagnostics[template] == source.INVALID_DIAGNOSTIC {
|
||||
pointer_type, _, ok := function_pointer_type_for_template(checker, template, demanded)
|
||||
if ok {
|
||||
last = pointer_type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Negate:
|
||||
stack[frame_index].stage = 5
|
||||
@@ -1002,14 +1116,60 @@ infer_expr :: proc(
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Infer_Frame{expr=expr.left, template=ast.INVALID_FUNCTION})
|
||||
case .Call:
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
callee_type := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = function_type
|
||||
stack[frame_index].args = make([]types.Type, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 6
|
||||
if len(expr.args) > 0 {
|
||||
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
|
||||
} else if valid_callable_arity(function_item, 0) {
|
||||
last = function_item.child
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
continue
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
template := ast.INVALID_FUNCTION
|
||||
if available {
|
||||
template = find_template(checker, expr.name, target_pkg)
|
||||
}
|
||||
if template == ast.INVALID_FUNCTION {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
callee_type := types.INVALID
|
||||
if !symbol.is_valid(expr.qualifier) {
|
||||
callee_type = find_infer_local(locals, expr.name)
|
||||
}
|
||||
if !types.is_valid(callee_type) && available {
|
||||
global := find_global(checker, expr.name, target_pkg)
|
||||
if global != ast.INVALID_GLOBAL {
|
||||
callee_type = checker.global_types[global]
|
||||
}
|
||||
}
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = function_type
|
||||
stack[frame_index].args = make([]types.Type, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 6
|
||||
if len(expr.args) > 0 {
|
||||
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
|
||||
} else if valid_callable_arity(function_item, 0) {
|
||||
last = function_item.child
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(checker.ast_module.functions[template].unsupported_reason) > 0 {
|
||||
@@ -1091,6 +1251,25 @@ infer_expr :: proc(
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
if frame.stage == 6 {
|
||||
if frame.arg_index < len(expr.args) {
|
||||
stack[frame_index].args[frame.arg_index] = last
|
||||
stack[frame_index].arg_index += 1
|
||||
if frame.arg_index+1 < len(expr.args) {
|
||||
append(&stack, Infer_Frame{expr=expr.args[frame.arg_index+1], template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
}
|
||||
function_item, ok := types.node(&checker.module.types, frame.left)
|
||||
if ok && function_item.kind == .Function && valid_callable_arity(function_item, len(expr.args)) {
|
||||
last = function_item.child
|
||||
} else {
|
||||
last = types.INVALID
|
||||
}
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
||||
@@ -1548,6 +1727,65 @@ find_struct_field :: proc(checker: ^Checker, struct_type: types.Type, name: symb
|
||||
return 0, {}, false
|
||||
}
|
||||
|
||||
build_function_value :: proc(
|
||||
checker: ^Checker,
|
||||
template: ast.Function_Id,
|
||||
span: source.Span,
|
||||
expected: types.Type,
|
||||
) -> hir.Expr_Id {
|
||||
if template == ast.INVALID_FUNCTION || int(template) >= len(checker.ast_module.functions) {
|
||||
return hir.INVALID_EXPR
|
||||
}
|
||||
function := checker.ast_module.functions[template]
|
||||
if len(function.unsupported_reason) > 0 {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"C declaration '%s' is unavailable: %s",
|
||||
symbol_text(checker, function.name),
|
||||
function.unsupported_reason,
|
||||
)
|
||||
return invalid_hir_expr(checker, span, id)
|
||||
}
|
||||
if checker.template_diagnostics[template] != source.INVALID_DIAGNOSTIC {
|
||||
return invalid_hir_expr(checker, span, checker.template_diagnostics[template])
|
||||
}
|
||||
params, result, ok := function_value_signature(checker, template)
|
||||
if !ok {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"function '%s' cannot be used as a C callback; expected a concrete c_func",
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
return invalid_hir_expr(checker, span, id)
|
||||
}
|
||||
defer delete(params, checker.allocator)
|
||||
function_type := types.function(&checker.module.types, params, result, true, function.variadic)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, true)
|
||||
spec := find_spec(checker, template, params)
|
||||
if spec == INVALID_SPEC {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"could not resolve callback specialization of '%s'",
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
return invalid_hir_expr(checker, span, id, pointer_type)
|
||||
}
|
||||
function_id := checker.specs[spec].hir_id
|
||||
assert(function_id != hir.INVALID_FUNCTION)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Function,
|
||||
span=span,
|
||||
type=pointer_type,
|
||||
target=hir.function_ref(function_id),
|
||||
left=hir.INVALID_EXPR,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
|
||||
build_nested_expr :: proc(
|
||||
checker: ^Checker,
|
||||
expr_id: ast.Expr_Id,
|
||||
@@ -1776,16 +2014,18 @@ build_compound_expr :: proc(
|
||||
case .Struct_Literal:
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
struct_type := types.find_named(store, u32(target_pkg), u32(expr.name)) if available else types.INVALID
|
||||
if !types.is_struct(struct_type, store) || types.is_opaque_struct(struct_type, store) {
|
||||
id := source.addf(checker.diagnostics, expr.span, "unknown or opaque struct type '%s'", symbol_text(checker, expr.name))
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
if types.is_c_struct(struct_type, store) {
|
||||
id := source.add(checker.diagnostics, expr.span, "c_struct values cannot be constructed by value")
|
||||
struct_type = types.resolve_alias(struct_type, store)
|
||||
if !types.is_record(struct_type, store) || types.is_opaque_struct(struct_type, store) {
|
||||
id := source.addf(checker.diagnostics, expr.span, "unknown or opaque record type '%s'", symbol_text(checker, expr.name))
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
fields := types.fields_for(store, struct_type)
|
||||
values := make([]hir.Expr_Id, len(fields), checker.allocator)
|
||||
union_record := types.is_union(struct_type, store)
|
||||
if union_record && len(expr.args) != 1 {
|
||||
id := source.add(checker.diagnostics, expr.span, "union literal requires exactly one field initializer")
|
||||
return invalid_hir_expr(checker, expr.span, id, struct_type)
|
||||
}
|
||||
values := make([]hir.Expr_Id, 1 if union_record else len(fields), checker.allocator)
|
||||
initialized := make([]bool, len(fields), checker.allocator)
|
||||
defer delete(initialized, checker.allocator)
|
||||
for &value in values {
|
||||
@@ -1803,18 +2043,35 @@ build_compound_expr :: proc(
|
||||
continue
|
||||
}
|
||||
initialized[index] = true
|
||||
values[index] = build_nested_expr(checker, keyed_expr.left, locals, global_reads, calls, field.type, pkg, file)
|
||||
values[index] = coerce_expr(checker, values[index], field.type, keyed_expr.span)
|
||||
value_index := 0 if union_record else index
|
||||
values[value_index] = build_nested_expr(checker, keyed_expr.left, locals, global_reads, calls, field.type, pkg, file)
|
||||
values[value_index] = coerce_expr(checker, values[value_index], field.type, keyed_expr.span)
|
||||
}
|
||||
for field, index in fields {
|
||||
if values[index] == hir.INVALID_EXPR {
|
||||
if !union_record {
|
||||
for field, index in fields {
|
||||
if values[index] != hir.INVALID_EXPR {
|
||||
continue
|
||||
}
|
||||
id := source.addf(checker.diagnostics, expr.span, "missing initializer for struct field '%s'", symbol_text(checker, symbol.Id(field.name)))
|
||||
delete(values, checker.allocator)
|
||||
return invalid_hir_expr(checker, expr.span, id, struct_type)
|
||||
}
|
||||
}
|
||||
active_field: i64
|
||||
if union_record {
|
||||
if values[0] == hir.INVALID_EXPR {
|
||||
delete(values, checker.allocator)
|
||||
return invalid_hir_expr(checker, expr.span, source.add(checker.diagnostics, expr.span, "union literal requires a known field"), struct_type)
|
||||
}
|
||||
for value, index in initialized {
|
||||
if value {
|
||||
active_field = i64(index)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Struct, span=expr.span, type=struct_type, args=values,
|
||||
kind=.Struct, span=expr.span, type=struct_type, args=values, integer=active_field,
|
||||
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
@@ -1938,11 +2195,16 @@ build_expr :: proc(
|
||||
target=hir.global_ref(hir_global), left = hir.INVALID_EXPR, right = hir.INVALID_EXPR, diagnostic = source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = add_name_resolution_diagnostic(checker, expr, target_pkg)
|
||||
template := find_template(checker, expr.name, target_pkg)
|
||||
if template != ast.INVALID_FUNCTION && checker.ast_module.functions[template].c_abi {
|
||||
last = build_function_value(checker, template, expr.span, frame.expected)
|
||||
} else {
|
||||
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = add_name_resolution_diagnostic(checker, expr, target_pkg)
|
||||
}
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
}
|
||||
_ = pop(&stack)
|
||||
@@ -1953,6 +2215,11 @@ build_expr :: proc(
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
case .Call:
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
stack[frame_index].stage = 6
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
id := add_package_resolution_diagnostic(checker, expr, file)
|
||||
@@ -1962,12 +2229,63 @@ build_expr :: proc(
|
||||
}
|
||||
template := find_template(checker, expr.name, target_pkg)
|
||||
if template == ast.INVALID_FUNCTION {
|
||||
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = add_call_resolution_diagnostic(checker, expr, target_pkg)
|
||||
callee := hir.INVALID_EXPR
|
||||
callee_from_global := false
|
||||
if !symbol.is_valid(expr.qualifier) {
|
||||
if local, ok := find_build_local(locals, expr.name); ok {
|
||||
callee = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Local, span=expr.span, type=local.type, target=hir.local_ref(local.id),
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
}
|
||||
if callee == hir.INVALID_EXPR {
|
||||
if global := find_global(checker, expr.name, target_pkg); global != ast.INVALID_GLOBAL {
|
||||
hir_global := hir.Global_Id(global)
|
||||
add_unique_global(global_reads, hir_global)
|
||||
callee = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Global, span=expr.span, type=checker.global_types[global],
|
||||
target=hir.global_ref(hir_global), left=hir.INVALID_EXPR,
|
||||
right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
callee_from_global = true
|
||||
}
|
||||
}
|
||||
if callee == hir.INVALID_EXPR {
|
||||
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = add_call_resolution_diagnostic(checker, expr, target_pkg)
|
||||
}
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
_, function_item, function_type, ok := types.function_pointer(checker.module.exprs[callee].type, &checker.module.types)
|
||||
if !ok {
|
||||
id := add_call_resolution_diagnostic(checker, expr, target_pkg) if callee_from_global else
|
||||
source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if !valid_callable_arity(function_item, len(expr.args)) {
|
||||
message := "function pointer expects at least %d arguments, got %d" if function_item.variadic else
|
||||
"function pointer expects %d arguments, got %d"
|
||||
id := source.addf(checker.diagnostics, expr.span, message, function_item.field_count, len(expr.args))
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = callee
|
||||
stack[frame_index].built_args = make([]hir.Expr_Id, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 7
|
||||
if len(expr.args) > 0 {
|
||||
arg_expected := callable_arg_expected(function_type, function_item, &checker.module.types, 0)
|
||||
if !is_runtime_type(checker, arg_expected) {
|
||||
arg_expected = types.INVALID
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.args[0], expected=arg_expected, template=ast.INVALID_FUNCTION})
|
||||
}
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if len(checker.ast_module.functions[template].unsupported_reason) > 0 {
|
||||
@@ -2143,6 +2461,95 @@ build_expr :: proc(
|
||||
}
|
||||
_ = pop(&stack)
|
||||
}
|
||||
if frame.stage == 6 {
|
||||
callee := last
|
||||
_, function_item, function_type, ok := types.function_pointer(checker.module.exprs[callee].type, &checker.module.types)
|
||||
if !ok {
|
||||
id := source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if !valid_callable_arity(function_item, len(expr.args)) {
|
||||
message := "function pointer expects at least %d arguments, got %d" if function_item.variadic else
|
||||
"function pointer expects %d arguments, got %d"
|
||||
id := source.addf(checker.diagnostics, expr.span, message, function_item.field_count, len(expr.args))
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = callee
|
||||
stack[frame_index].built_args = make([]hir.Expr_Id, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 7
|
||||
if len(expr.args) > 0 {
|
||||
arg_expected := callable_arg_expected(function_type, function_item, &checker.module.types, 0)
|
||||
if !is_runtime_type(checker, arg_expected) {
|
||||
arg_expected = types.INVALID
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.args[0], expected=arg_expected, template=ast.INVALID_FUNCTION})
|
||||
}
|
||||
continue
|
||||
}
|
||||
if frame.stage == 7 {
|
||||
if frame.arg_index < len(expr.args) {
|
||||
stack[frame_index].built_args[frame.arg_index] = last
|
||||
stack[frame_index].arg_index += 1
|
||||
if frame.arg_index+1 < len(expr.args) {
|
||||
next := frame.arg_index+1
|
||||
callee_type := checker.module.exprs[frame.left].type
|
||||
_, function_item, function_type, _ := types.function_pointer(callee_type, &checker.module.types)
|
||||
arg_expected := callable_arg_expected(function_type, function_item, &checker.module.types, next)
|
||||
if !is_runtime_type(checker, arg_expected) {
|
||||
arg_expected = types.INVALID
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.args[next], expected=arg_expected, template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
}
|
||||
callee_type := checker.module.exprs[frame.left].type
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
id := source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
|
||||
delete(stack[frame_index].built_args, checker.allocator)
|
||||
stack[frame_index].built_args = nil
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
fixed_count := int(function_item.field_count)
|
||||
for index in 0..<min(fixed_count, len(stack[frame_index].built_args)) {
|
||||
expected_arg := callable_arg_expected(function_type, function_item, &checker.module.types, index)
|
||||
stack[frame_index].built_args[index] = coerce_expr(
|
||||
checker,
|
||||
stack[frame_index].built_args[index],
|
||||
expected_arg,
|
||||
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,
|
||||
)
|
||||
}
|
||||
result := function_item.child
|
||||
if !types.is_valid(result) {
|
||||
id := source.add(checker.diagnostics, expr.span, "could not resolve function pointer result type")
|
||||
delete(stack[frame_index].built_args, checker.allocator)
|
||||
stack[frame_index].built_args = nil
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
} else {
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Call, span=expr.span, type=result, target=hir.INVALID_REF,
|
||||
left=frame.left, right=hir.INVALID_EXPR, args=stack[frame_index].built_args,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
stack[frame_index].built_args = nil
|
||||
}
|
||||
_ = pop(&stack)
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user