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:
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