aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 13:30:00 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 13:30:00 +0000
commit90bf56f456e22dcd98458eca14e0b152a5efb179 (patch)
tree629f46a15ea13d1d71998355f00d77d5c80052a3
parent4a70e44f06e637762eec0fd875eb94b84c3f4037 (diff)
feat(codegen): accept record pointers as variadic arguments
Pointer types to C-struct records now pass unchanged as variadic EXTERN arguments. Pseudocode values still need a cast to a C type, and records by value stay rejected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--lib/CodeGen/CallGen.cc3
-rw-r--r--tests/i/extern-c-struct/expected.txt1
-rw-r--r--tests/i/extern-c-struct/lib.c12
-rw-r--r--tests/i/extern-c-struct/notes.md2
-rw-r--r--tests/i/extern-c-struct/program.cps4
5 files changed, 20 insertions, 2 deletions
diff --git a/lib/CodeGen/CallGen.cc b/lib/CodeGen/CallGen.cc
index dfe89d6..b25ca12 100644
--- a/lib/CodeGen/CallGen.cc
+++ b/lib/CodeGen/CallGen.cc
@@ -128,7 +128,8 @@ bool CodeGen::marshalCallArgs(const FuncSig &Signature,
// C values are accepted, matching the explicit-cast rule for C/pseudocode.
Value *CodeGen::emitVariadicArgument(ExprAST *Expr, const std::string &Callee, size_t Position) {
const TypeInfo *Info = isWholeArrayVar(Expr) ? nullptr : getExprTypeInfo(Expr);
- if (!Info || !Info->isC() || Info->isVoid()) {
+ // Pointers to C structs pass unchanged; records by value are not supported yet.
+ if (!Info || Info->isRecord() || !Types->isExternType(Info)) {
reportError("Variadic argument %zu to %s must be a C value; cast it to a C type", Position, Callee.c_str());
return nullptr;
}
diff --git a/tests/i/extern-c-struct/expected.txt b/tests/i/extern-c-struct/expected.txt
index 62e50ed..dee8467 100644
--- a/tests/i/extern-c-struct/expected.txt
+++ b/tests/i/extern-c-struct/expected.txt
@@ -1,3 +1,4 @@
S 4.500000 5
G 0.500000 7
9
+14
diff --git a/tests/i/extern-c-struct/lib.c b/tests/i/extern-c-struct/lib.c
index 39380ec..65080f1 100644
--- a/tests/i/extern-c-struct/lib.c
+++ b/tests/i/extern-c-struct/lib.c
@@ -1,3 +1,5 @@
+#include <stdarg.h>
+
struct Sample {
char tag;
double value;
@@ -9,3 +11,13 @@ static struct Sample shared = {'G', 0.5, 7};
void scale_sample(struct Sample *s, int k) { s->value *= k; }
struct Sample *shared_sample(void) { return &shared; }
int shared_count(void) { return shared.count; }
+
+/* Each argument after n is a struct Sample *, read back with va_arg. */
+int sum_counts(int n, ...) {
+ va_list ap;
+ int total = 0;
+ va_start(ap, n);
+ for (int i = 0; i < n; ++i) total += va_arg(ap, struct Sample *)->count;
+ va_end(ap);
+ return total;
+}
diff --git a/tests/i/extern-c-struct/notes.md b/tests/i/extern-c-struct/notes.md
index 1a0d215..519e835 100644
--- a/tests/i/extern-c-struct/notes.md
+++ b/tests/i/extern-c-struct/notes.md
@@ -4,4 +4,4 @@ Human review: pending. Expected output was independently traced by AI, not captu
Basis: compiler extension (EXTERN "C", INCLUDE_OBJ of a user object); a record whose fields are all C types has the layout of the matching C struct.
-Trace: the harness compiles `lib.c` with the host C compiler. Sample matches `struct Sample {char; double; int}`, including the padding after the char. S is 'S', 1.5, 5. scale_sample(^S, 3) multiplies the value through the pointer to 4.5 and leaves tag and count alone, so the first line is `S 4.500000 5`. shared_sample returns the address of the static struct in lib.c, initialised to 'G', 0.5, 7, giving `G 0.500000 7`. P^.Count <- 9 writes through that pointer into the C object, so shared_count() returns 9.
+Trace: the harness compiles `lib.c` with the host C compiler. Sample matches `struct Sample {char; double; int}`, including the padding after the char. S is 'S', 1.5, 5. scale_sample(^S, 3) multiplies the value through the pointer to 4.5 and leaves tag and count alone, so the first line is `S 4.500000 5`. shared_sample returns the address of the static struct in lib.c, initialised to 'G', 0.5, 7, giving `G 0.500000 7`. P^.Count <- 9 writes through that pointer into the C object, so shared_count() returns 9. sum_counts receives P and Q as variadic arguments; pointers are passed unchanged and read back with va_arg(ap, struct Sample *). P points to the shared struct, whose count is now 9, and Q points to S, whose count is 5, so the last line is 9 + 5 = 14.
diff --git a/tests/i/extern-c-struct/program.cps b/tests/i/extern-c-struct/program.cps
index 1954d29..bcb7af4 100644
--- a/tests/i/extern-c-struct/program.cps
+++ b/tests/i/extern-c-struct/program.cps
@@ -9,8 +9,10 @@ TYPE PSample = ^Sample
EXTERN "C" EXTERN_PROCEDURE scale_sample(PSample, C_INT)
EXTERN "C" EXTERN_FUNCTION shared_sample() RETURNS PSample
EXTERN "C" EXTERN_FUNCTION shared_count() RETURNS C_INT
+EXTERN "C" EXTERN_FUNCTION sum_counts(C_INT, ...) RETURNS C_INT
DECLARE S : Sample
DECLARE P : PSample
+DECLARE Q : PSample
S.Tag <- (C_CHAR)'S'
S.Value <- (C_DOUBLE)1.5
S.Count <- (C_INT)5
@@ -20,3 +22,5 @@ P <- shared_sample()
OUTPUT P^.Tag, " ", P^.Value, " ", P^.Count
P^.Count <- (C_INT)9
OUTPUT shared_count()
+Q <- ^S
+OUTPUT sum_counts((C_INT)2, P, Q)