kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit df65c98be9835c3b071228f110f27524af18a964
parent 14ba583ce257058b9eeccf580e289a3487c8f371
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat, 16 May 2026 09:45:35 -0700

Add Toy function pointer calls

Diffstat:
Mlang/toy/toy.c | 272++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/api/cg.c | 14+++++++++++---
Atest/toy/cases/31_fn_pointer_call.expected | 1+
Atest/toy/cases/31_fn_pointer_call.toy | 9+++++++++
4 files changed, 216 insertions(+), 80 deletions(-)

diff --git a/lang/toy/toy.c b/lang/toy/toy.c @@ -529,6 +529,67 @@ static CfreeCgSym toy_find_decl_sym(ToyParser* p, CfreeSym name) { * ============================================================ */ static CfreeCgTypeId toy_parse_type(ToyParser* p) { + if (toy_parser_match(p, TOK_FN)) { + CfreeCgTypeId param_types[TOY_MAX_PARAMS]; + CfreeCgFuncParam sig_params[TOY_MAX_PARAMS]; + CfreeCgFuncSig sig; + CfreeCgTypeId ret_type; + size_t nparams = 0; + int variadic = 0; + size_t i; + + if (!toy_parser_expect(p, TOK_LPAREN)) { + toy_error(p, p->cur.loc, "expected '(' after function type"); + return CFREE_CG_TYPE_NONE; + } + if (p->cur.kind != TOK_RPAREN) { + for (;;) { + if (p->cur.kind == TOK_DOTDOTDOT) { + variadic = 1; + toy_parser_advance(p); + break; + } + if (nparams >= TOY_MAX_PARAMS) { + toy_error(p, p->cur.loc, "too many function type parameters"); + return CFREE_CG_TYPE_NONE; + } + param_types[nparams] = toy_parse_type(p); + if (param_types[nparams] == CFREE_CG_TYPE_NONE) + return CFREE_CG_TYPE_NONE; + nparams++; + if (p->cur.kind == TOK_COMMA) { + toy_parser_advance(p); + if (p->cur.kind == TOK_DOTDOTDOT) { + variadic = 1; + toy_parser_advance(p); + break; + } + } else { + break; + } + } + } + if (!toy_parser_expect(p, TOK_RPAREN)) { + toy_error(p, p->cur.loc, "expected ')' after function type parameters"); + return CFREE_CG_TYPE_NONE; + } + if (!toy_parser_expect(p, TOK_COLON)) { + toy_error(p, p->cur.loc, "expected ':' before function return type"); + return CFREE_CG_TYPE_NONE; + } + ret_type = toy_parse_type(p); + if (ret_type == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; + + memset(sig_params, 0, sizeof sig_params); + for (i = 0; i < nparams; i++) sig_params[i].type = param_types[i]; + memset(&sig, 0, sizeof sig); + sig.ret = ret_type; + sig.params = sig_params; + sig.nparams = (uint32_t)nparams; + sig.call_conv = CFREE_CG_CC_TARGET_C; + sig.abi_variadic = variadic; + return cfree_cg_type_func(p->c, sig); + } if (toy_parser_match(p, TOK_LBRACKET)) { uint64_t count; CfreeCgTypeId elem; @@ -614,6 +675,84 @@ static CfreeCgTypeId toy_parse_type(ToyParser* p) { * ============================================================ */ static CfreeCgTypeId toy_parse_expr(ToyParser* p); +static CfreeCgMemAccess toy_mem_access(ToyParser* p, CfreeCgTypeId type); + +static CfreeCgTypeId toy_ptr_pointee_func_type(ToyParser* p, + CfreeCgTypeId ptr_ty) { + CfreeCgTypeId fn_ty; + if (cfree_cg_type_kind(p->c, ptr_ty) != CFREE_CG_TYPE_PTR) + return CFREE_CG_TYPE_NONE; + fn_ty = cfree_cg_type_ptr_pointee(p->c, ptr_ty); + if (cfree_cg_type_kind(p->c, fn_ty) != CFREE_CG_TYPE_FUNC) + return CFREE_CG_TYPE_NONE; + return fn_ty; +} + +static CfreeCgTypeId toy_push_named_rvalue(ToyParser* p, CfreeSym name) { + ToyVar* v = toy_find_var(p, name); + if (v) { + toy_push_var_lvalue(p, v); + cfree_cg_load(p->cg, toy_mem_access(p, v->type)); + return v->type; + } + { + ToyGlobal* g = toy_find_global(p, name); + if (g) { + cfree_cg_push_symbol_lvalue(p->cg, g->sym, 0); + cfree_cg_load(p->cg, toy_mem_access(p, g->type)); + return g->type; + } + } + { + ToyFn* fn = toy_find_fn(p, name); + if (fn) { + cfree_cg_push_symbol_addr(p->cg, fn->sym, 0); + return cfree_cg_type_ptr(p->c, fn->type, 0); + } + } + return CFREE_CG_TYPE_NONE; +} + +static int toy_parse_call_args(ToyParser* p, ToyToken call_tok, + CfreeCgTypeId fn_ty, size_t* out_nargs) { + uint32_t nparams = cfree_cg_type_func_nparams(p->c, fn_ty); + int variadic = cfree_cg_type_func_is_variadic(p->c, fn_ty); + size_t nargs = 0; + if (p->cur.kind != TOK_RPAREN) { + for (;;) { + CfreeCgTypeId arg_ty; + if (nargs >= TOY_MAX_PARAMS) { + toy_error(p, p->cur.loc, "too many arguments"); + return 0; + } + arg_ty = toy_parse_expr(p); + if (arg_ty == CFREE_CG_TYPE_NONE) return 0; + if (nargs < nparams) { + CfreeCgFuncParam param = + cfree_cg_type_func_param(p->c, fn_ty, (uint32_t)nargs); + if (arg_ty != param.type) { + toy_error(p, call_tok.loc, "function argument type mismatch"); + return 0; + } + } else if (!variadic) { + toy_error(p, call_tok.loc, "too many arguments"); + return 0; + } + nargs++; + if (!toy_parser_match(p, TOK_COMMA)) break; + } + } + if (!toy_parser_expect(p, TOK_RPAREN)) { + toy_error(p, p->cur.loc, "expected ')' after arguments"); + return 0; + } + if (nargs < nparams) { + toy_error(p, call_tok.loc, "too few arguments"); + return 0; + } + *out_nargs = nargs; + return 1; +} static int toy_sym_is(ToyParser* p, CfreeSym sym, const char* name) { return sym == cfree_sym_intern(p->c, name); @@ -1752,67 +1891,36 @@ static CfreeCgTypeId toy_parse_expr_primary(ToyParser* p) { if (recognized) return builtin_ty; ToyFn* fn = toy_find_fn(p, name); - if (!fn) { - toy_error(p, ident_tok.loc, "undefined function '%s'", - (const char*)ident_tok.text); - return CFREE_CG_TYPE_NONE; - } toy_parser_advance(p); /* ( */ - CfreeCgTypeId arg_types[TOY_MAX_PARAMS]; size_t nargs = 0; - if (p->cur.kind != TOK_RPAREN) { - for (;;) { - if (nargs >= TOY_MAX_PARAMS) { - toy_error(p, p->cur.loc, "too many arguments"); - return CFREE_CG_TYPE_NONE; - } - CfreeCgTypeId arg_ty = toy_parse_expr(p); - if (arg_ty == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; - arg_types[nargs++] = arg_ty; - if (p->cur.kind == TOK_COMMA) { - toy_parser_advance(p); - } else { - break; - } - } - } - if (!toy_parser_expect(p, TOK_RPAREN)) { - toy_error(p, p->cur.loc, "expected ')' after arguments"); - return CFREE_CG_TYPE_NONE; + if (fn) { + if (!toy_parse_call_args(p, ident_tok, fn->type, &nargs)) + return CFREE_CG_TYPE_NONE; + cfree_cg_call_symbol_default(p->cg, fn->sym, (uint32_t)nargs); + return fn->ret; } - /* Verify argument count */ - if ((!fn->variadic && nargs != fn->nparams) || - (fn->variadic && nargs < fn->nparams)) { - toy_error(p, ident_tok.loc, - "function '%s' expects %zu arguments, got %zu", - (const char*)ident_tok.text, fn->nparams, nargs); + CfreeCgTypeId callee_ty = toy_push_named_rvalue(p, name); + if (callee_ty == CFREE_CG_TYPE_NONE) { + toy_error(p, ident_tok.loc, "undefined function '%s'", + (const char*)ident_tok.text); return CFREE_CG_TYPE_NONE; } - for (size_t i = 0; i < fn->nparams; ++i) { - if (arg_types[i] != fn->params[i]) { - toy_error(p, ident_tok.loc, "function argument type mismatch"); - return CFREE_CG_TYPE_NONE; - } + CfreeCgTypeId fn_ty = toy_ptr_pointee_func_type(p, callee_ty); + if (fn_ty == CFREE_CG_TYPE_NONE) { + toy_error(p, ident_tok.loc, "callee is not a function pointer"); + return CFREE_CG_TYPE_NONE; } - - cfree_cg_call_symbol_default(p->cg, fn->sym, (uint32_t)nargs); - return fn->ret; + if (!toy_parse_call_args(p, ident_tok, fn_ty, &nargs)) + return CFREE_CG_TYPE_NONE; + cfree_cg_call_default(p->cg, (uint32_t)nargs, fn_ty); + return cfree_cg_type_func_ret(p->c, fn_ty); } - /* Variable reference */ - ToyVar* v = toy_find_var(p, name); - if (v) { - toy_push_var_lvalue(p, v); - cfree_cg_load(p->cg, toy_mem_access(p, v->type)); - return v->type; - } - ToyGlobal* g = toy_find_global(p, name); - if (g) { - cfree_cg_push_symbol_lvalue(p->cg, g->sym, 0); - cfree_cg_load(p->cg, toy_mem_access(p, g->type)); - return g->type; + { + CfreeCgTypeId ty = toy_push_named_rvalue(p, name); + if (ty != CFREE_CG_TYPE_NONE) return ty; } toy_error(p, ident_tok.loc, "undefined variable '%s'", (const char*)ident_tok.text); @@ -1886,12 +1994,18 @@ static CfreeCgTypeId toy_parse_expr_unary(ToyParser* p) { return cfree_cg_type_ptr(p->c, v->type, 0); } else { ToyGlobal* g = toy_find_global(p, name); - if (!g) { - toy_error(p, p->cur.loc, "undefined variable"); - return CFREE_CG_TYPE_NONE; + if (g) { + cfree_cg_push_symbol_addr(p->cg, g->sym, 0); + return cfree_cg_type_ptr(p->c, g->type, 0); + } else { + ToyFn* fn = toy_find_fn(p, name); + if (fn) { + cfree_cg_push_symbol_addr(p->cg, fn->sym, 0); + return cfree_cg_type_ptr(p->c, fn->type, 0); + } } - cfree_cg_push_symbol_addr(p->cg, g->sym, 0); - return cfree_cg_type_ptr(p->c, g->type, 0); + toy_error(p, p->cur.loc, "undefined variable"); + return CFREE_CG_TYPE_NONE; } } @@ -2370,41 +2484,45 @@ static int toy_parse_return_stmt(ToyParser* p) { if (toy_parser_match(p, TOK_TAIL)) { CfreeSym name; ToyFn* fn; + ToyToken call_tok; + CfreeCgTypeId fn_ty; size_t nargs = 0; if (p->cur.kind != TOK_IDENT) { toy_error(p, p->cur.loc, "expected function name after tail"); return 0; } + call_tok = p->cur; name = toy_tok_sym(p, p->cur); fn = toy_find_fn(p, name); - if (!fn) { - toy_error(p, p->cur.loc, "undefined function in tail call"); - return 0; - } - if (fn->variadic) { - toy_error(p, p->cur.loc, "tail call to variadic function unsupported"); - return 0; - } toy_parser_advance(p); if (!toy_parser_expect(p, TOK_LPAREN)) return 0; - if (p->cur.kind != TOK_RPAREN) { - for (;;) { - CfreeCgTypeId arg_ty = toy_parse_expr(p); - if (arg_ty == CFREE_CG_TYPE_NONE) return 0; - if (nargs >= fn->nparams || arg_ty != fn->params[nargs]) { - toy_error(p, p->cur.loc, "tail call argument mismatch"); - return 0; - } - nargs++; - if (!toy_parser_match(p, TOK_COMMA)) break; + if (fn) { + fn_ty = fn->type; + } else { + CfreeCgTypeId callee_ty = toy_push_named_rvalue(p, name); + if (callee_ty == CFREE_CG_TYPE_NONE) { + toy_error(p, call_tok.loc, "undefined function in tail call"); + return 0; } + fn_ty = toy_ptr_pointee_func_type(p, callee_ty); + if (fn_ty == CFREE_CG_TYPE_NONE) { + toy_error(p, call_tok.loc, "tail callee is not a function pointer"); + return 0; + } + } + if (cfree_cg_type_func_is_variadic(p->c, fn_ty)) { + toy_error(p, call_tok.loc, "tail call to variadic function unsupported"); + return 0; } - if (!toy_parser_expect(p, TOK_RPAREN)) return 0; - if (nargs != fn->nparams || fn->ret != p->cur_fn_ret) { + if (!toy_parse_call_args(p, call_tok, fn_ty, &nargs)) return 0; + if (cfree_cg_type_func_ret(p->c, fn_ty) != p->cur_fn_ret) { toy_error(p, p->cur.loc, "tail call signature mismatch"); return 0; } - cfree_cg_tail_call_symbol(p->cg, fn->sym, (uint32_t)nargs); + if (fn) + cfree_cg_tail_call_symbol(p->cg, fn->sym, (uint32_t)nargs); + else + cfree_cg_tail_call(p->cg, (uint32_t)nargs, fn_ty); if (!toy_parser_expect(p, TOK_SEMI)) return 0; return 1; } diff --git a/src/api/cg.c b/src/api/cg.c @@ -5262,7 +5262,9 @@ void cfree_cg_call(CfreeCg *g, uint32_t nargs, CfreeCgTypeId fn_type, avs[idx].size = abi_cg_sizeof(g->c->abi, aty); } else { avs[idx].storage = - api_is_lvalue_sv(&arg) ? api_force_reg(g, &arg, aty) : arg.op; + (api_is_lvalue_sv(&arg) || arg.op.kind == OPK_GLOBAL) + ? api_force_reg(g, &arg, aty) + : arg.op; } } @@ -5298,6 +5300,8 @@ void cfree_cg_call(CfreeCg *g, uint32_t nargs, CfreeCgTypeId fn_type, Reg r = api_alloc_reg_or_spill(g, api_type_class(ret_ty), ret_ty); desc.ret.storage = api_op_reg(r, ret_ty); } + } else { + desc.ret.storage = api_op_imm(0, builtin_id(CFREE_CG_BUILTIN_VOID)); } T->call(T, &desc); @@ -5356,7 +5360,9 @@ static void api_cg_tail_call(CfreeCg *g, uint32_t nargs, avs[idx].type = aty; avs[idx].abi = idx < abi->nparams ? &abi->params[idx] : NULL; avs[idx].storage = - api_is_lvalue_sv(&arg) ? api_force_reg(g, &arg, aty) : arg.op; + (api_is_lvalue_sv(&arg) || arg.op.kind == OPK_GLOBAL) + ? api_force_reg(g, &arg, aty) + : arg.op; } callee = api_pop(g); api_ensure_reg(g, &callee); @@ -5434,7 +5440,9 @@ static void api_call_symbol_common(CfreeCg *g, CfreeCgSym sym, uint32_t nargs, avs[idx].size = abi_cg_sizeof(g->c->abi, aty); } else { avs[idx].storage = - api_is_lvalue_sv(&arg) ? api_force_reg(g, &arg, aty) : arg.op; + (api_is_lvalue_sv(&arg) || arg.op.kind == OPK_GLOBAL) + ? api_force_reg(g, &arg, aty) + : arg.op; } } callee_op = api_op_global((ObjSymId)sym, 0, cg_type_ptr_to(g->c, fty)); diff --git a/test/toy/cases/31_fn_pointer_call.expected b/test/toy/cases/31_fn_pointer_call.expected @@ -0,0 +1 @@ +44 diff --git a/test/toy/cases/31_fn_pointer_call.toy b/test/toy/cases/31_fn_pointer_call.toy @@ -0,0 +1,9 @@ +fn add2(x: int): int { + return x + 2; +} + +fn main(): int { + let fp: *fn(int): int = add2; + let fp2: *fn(int): int = &add2; + return fp(20) + fp2(20); +}