kit

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

commit bce407c9dc13e5d0194531c760c36d738c92c131
parent 0f8f6f11206cb62dc958185dd0310b3135f15aa9
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 15 Jun 2026 17:47:15 -0700

cg: unevaluated call result must be a place for aggregate returns

The unevaluated call path (used to type-check a call without emitting code)
pushed every result via api_uneval_value -- a VALUE. api_push enforces that
an aggregate can only ever be a PLACE, so an aggregate-returning call in an
unevaluated context tripped "aggregate must be a place, not a value".

This surfaced compiling src/arch/riscv/isa.c: a compound literal whose first
field is a KitSlice returned by slice_from_cstr(), i.e. an aggregate field
initialized by a struct-returning call. The C frontend's initializer
type-compatibility probe (try_init_aggregate_from_expr) parses that call in an
unevaluated context.

Add api_push_uneval_call_result, mirroring api_push_call_result's place-vs-value
rule (api_uneval_place for aggregates, api_uneval_value for scalars), and use it
at both unevaluated call sites.

Regression test test/parse/cases/init_aggregate_field_from_call.c; passes E on
aa64/x64/rv64 at O0/O1.

Diffstat:
Msrc/cg/call.c | 23+++++++++++++++--------
Atest/parse/cases/init_aggregate_field_from_call.c | 35+++++++++++++++++++++++++++++++++++
Atest/parse/cases/init_aggregate_field_from_call.expected | 1+
3 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/src/cg/call.c b/src/cg/call.c @@ -129,6 +129,19 @@ void api_push_call_result(KitCg* g, CGLocal result, KitCgTypeId ret_ty) { } } +/* Push the placeholder for a call result in an unevaluated context (the call is + * type-checked but emits no code). Mirrors api_push_call_result's place-vs-value + * rule: an aggregate result must be a PLACE even here, since api_push rejects an + * aggregate VALUE (e.g. a struct-returning call in an initializer's type-compat + * probe). */ +static void api_push_uneval_call_result(KitCg* g, KitCgTypeId result_type) { + if (!api_type_has_value(g, result_type)) return; + api_push(g, cg_type_is_aggregate(g->c, result_type) + ? api_uneval_place(g, result_type) + : api_uneval_value(g, result_type)); + api_const_set_top(g, api_const_unknown(result_type)); +} + static void api_call_clobber_boundary(KitCg* g, const CGCallDesc* d) { (void)g; (void)d; @@ -213,10 +226,7 @@ void kit_cg_call(KitCg* g, uint32_t nargs, KitCgTypeId fn_type, } callee = api_pop(g); api_release(g, &callee); - if (api_type_has_value(g, result_type)) { - api_push(g, api_uneval_value(g, result_type)); - api_const_set_top(g, api_const_unknown(result_type)); - } + api_push_uneval_call_result(g, result_type); return; } @@ -279,10 +289,7 @@ void api_call_symbol_common(KitCg* g, KitCgSym sym, uint32_t nargs, ApiSValue arg = api_pop(g); api_release(g, &arg); } - if (api_type_has_value(g, result_type)) { - api_push(g, api_uneval_value(g, result_type)); - api_const_set_top(g, api_const_unknown(result_type)); - } + api_push_uneval_call_result(g, result_type); return; } args = api_alloc_call_args(g, nargs); diff --git a/test/parse/cases/init_aggregate_field_from_call.c b/test/parse/cases/init_aggregate_field_from_call.c @@ -0,0 +1,35 @@ +/* An aggregate-typed field initialized by a struct-returning call inside a + * braced initializer: `Wrap w = { mk(a), t };`. The initializer's + * type-compatibility probe parses the call in an unevaluated context, where the + * call result must still be pushed as a PLACE (an aggregate can never be a value + * on the CG stack). Pushing it as a value tripped "aggregate must be a place, + * not a value" while compiling src/arch/riscv/isa.c (a compound literal whose + * first field is a KitSlice returned by slice_from_cstr). Returns a checksum of + * the copied fields (expect 120). */ + +typedef struct { + int x, y; +} Pair; + +typedef struct { + Pair p; + int tag; +} Wrap; + +__attribute__((noinline)) static Pair mk(int a) { + Pair r; + r.x = a; + r.y = a * 2; + return r; +} + +__attribute__((noinline)) static Wrap build(int a, int t) { + Wrap w = {mk(a), t}; /* aggregate field <- struct-returning call */ + return w; +} + +int test_main(void) { + Wrap w = build(7, 99); + /* x=7, y=14, tag=99 -> 7 + 14 + 99 = 120 */ + return w.p.x + w.p.y + w.tag; +} diff --git a/test/parse/cases/init_aggregate_field_from_call.expected b/test/parse/cases/init_aggregate_field_from_call.expected @@ -0,0 +1 @@ +120