aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 13:51:44 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 13:51:44 +0000
commit5eccd90b47b327917ad0e5858c79ca8fab27cd74 (patch)
tree56e5c4110560679047e8813ab221742a0ab739f6 /tests
parente3cc6c585a15fe4478dd2312bf5d390896729a31 (diff)
feat(codegen): pass C-struct records by value as variadic arguments
A variadic EXTERN call is now arranged by clang per call, from the fixed parameter count and the promoted argument types. C-struct records pass unchanged in variadic positions, as C11 6.5.2.2p6-7 and 7.16.1.1p2 allow; a record that is not a C struct names the offending field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/i/extern-c-struct-value/expected.txt2
-rw-r--r--tests/i/extern-c-struct-value/lib.c24
-rw-r--r--tests/i/extern-c-struct-value/notes.md4
-rw-r--r--tests/i/extern-c-struct-value/program.cps9
-rw-r--r--tests/iii/extern-struct-rejected/case.json4
-rw-r--r--tests/iii/extern-struct-rejected/notes.md4
-rw-r--r--tests/iii/extern-struct-rejected/program.cps4
7 files changed, 45 insertions, 6 deletions
diff --git a/tests/i/extern-c-struct-value/expected.txt b/tests/i/extern-c-struct-value/expected.txt
index 19eef3c..f96c960 100644
--- a/tests/i/extern-c-struct-value/expected.txt
+++ b/tests/i/extern-c-struct-value/expected.txt
@@ -8,3 +8,5 @@
1.500000 2.000000
Q 0.250000 9
0.500000 1.250000 2.000000
+7.750000
+14
diff --git a/tests/i/extern-c-struct-value/lib.c b/tests/i/extern-c-struct-value/lib.c
index 94afc3f..d74a1d0 100644
--- a/tests/i/extern-c-struct-value/lib.c
+++ b/tests/i/extern-c-struct-value/lib.c
@@ -1,3 +1,5 @@
+#include <stdarg.h>
+
/* Each struct is passed or returned by value; the host C ABI decides how. */
struct Pair { int a, b; };
struct Vec2 { double x, y; };
@@ -22,3 +24,25 @@ struct Mixed make_mixed(char tag, double value, int count) {
return m;
}
struct Vec3f make_vec3f(float x, float y, float z) { struct Vec3f v = {x, y, z}; return v; }
+
+/* Each argument after n is a struct read back with va_arg. */
+double sum_vec2(int n, ...) {
+ va_list ap;
+ double total = 0;
+ va_start(ap, n);
+ for (int i = 0; i < n; ++i) {
+ struct Vec2 v = va_arg(ap, struct Vec2);
+ total += v.x + v.y;
+ }
+ va_end(ap);
+ return total;
+}
+
+int mixed_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 Mixed).count;
+ va_end(ap);
+ return total;
+}
diff --git a/tests/i/extern-c-struct-value/notes.md b/tests/i/extern-c-struct-value/notes.md
index a614d7b..c694d08 100644
--- a/tests/i/extern-c-struct-value/notes.md
+++ b/tests/i/extern-c-struct-value/notes.md
@@ -2,6 +2,6 @@
Human review: pending. Expected output was independently traced by AI, not captured from cpsc.
-Basis: compiler extension (EXTERN "C", INCLUDE_OBJ of a user object); a record whose fields are all C types is passed and returned by value like the matching C struct (C11 6.5.2.2p7: arguments are assigned to the parameters; 6.8.6.4p3: the returned value is the function call's value).
+Basis: compiler extension (EXTERN "C", INCLUDE_OBJ of a user object); a record whose fields are all C types is passed and returned by value like the matching C struct (C11 6.5.2.2p7: arguments are assigned to the parameters; 6.8.6.4p3: the returned value is the function call's value; 6.5.2.2p6-7 and 7.16.1.1p2: default promotions leave structs unchanged and va_arg reads a struct type).
-Trace: the harness compiles `lib.c` with the host C compiler. pair_sum receives Pair {3, 4} and returns 3 + 4 = 7. vec2_dot receives {1.5, 2} and {4, 0.25}: 1.5 * 4 + 2 * 0.25 = 6 + 0.5 = 6.5, printed like other C doubles as `6.500000`. mixed_score receives bias -1, Mixed {'M', 2.5, 3} and k 4: -1 + 2.5 * 4 + 3 + 1 (the tag is 'M') = 13, printed `13.000000`. vec3f_sum receives {0.5, 1.25, 2}, all exact in float, and returns 0.5 + 1.25 + 2 = 3.75, printed `3.750000`. wide_after_five receives five ints and Wide {100, 1000}: 1 + 2 + 3 + 4 + 5 + 100 + 1000 = 1115. On x86-64 System V the five ints leave one integer register, so the 16-byte Wide goes on the stack. The make_ functions return the struct built from their arguments: make_pair(3, 4) gives `3 4`, and pair_sum(make_pair(5, 6)) passes the returned Pair straight on, 5 + 6 = 11. make_vec2(1.5, 2) gives `1.500000 2.000000`. make_mixed('Q', 0.25, 9) gives `Q 0.250000 9`; on x86-64 System V this 24-byte struct comes back through memory supplied by the caller. make_vec3f(0.5, 1.25, 2) gives `0.500000 1.250000 2.000000`, the C floats printed with %f.
+Trace: the harness compiles `lib.c` with the host C compiler. pair_sum receives Pair {3, 4} and returns 3 + 4 = 7. vec2_dot receives {1.5, 2} and {4, 0.25}: 1.5 * 4 + 2 * 0.25 = 6 + 0.5 = 6.5, printed like other C doubles as `6.500000`. mixed_score receives bias -1, Mixed {'M', 2.5, 3} and k 4: -1 + 2.5 * 4 + 3 + 1 (the tag is 'M') = 13, printed `13.000000`. vec3f_sum receives {0.5, 1.25, 2}, all exact in float, and returns 0.5 + 1.25 + 2 = 3.75, printed `3.750000`. wide_after_five receives five ints and Wide {100, 1000}: 1 + 2 + 3 + 4 + 5 + 100 + 1000 = 1115. On x86-64 System V the five ints leave one integer register, so the 16-byte Wide goes on the stack. The make_ functions return the struct built from their arguments: make_pair(3, 4) gives `3 4`, and pair_sum(make_pair(5, 6)) passes the returned Pair straight on, 5 + 6 = 11. make_vec2(1.5, 2) gives `1.500000 2.000000`. make_mixed('Q', 0.25, 9) gives `Q 0.250000 9`; on x86-64 System V this 24-byte struct comes back through memory supplied by the caller. make_vec3f(0.5, 1.25, 2) gives `0.500000 1.250000 2.000000`, the C floats printed with %f. sum_vec2 reads two struct Vec2 values with va_arg: U is still {1.5, 2} from make_vec2 and V is {4, 0.25}, so 1.5 + 2 + 4 + 0.25 = 7.75, printed `7.750000`. mixed_counts reads two struct Mixed values: M holds count 9 from make_mixed and N = make_mixed('R', 1, 5) holds count 5, so 9 + 5 = 14.
diff --git a/tests/i/extern-c-struct-value/program.cps b/tests/i/extern-c-struct-value/program.cps
index 6b2cb8e..c8b3a1f 100644
--- a/tests/i/extern-c-struct-value/program.cps
+++ b/tests/i/extern-c-struct-value/program.cps
@@ -1,4 +1,5 @@
-// Records whose fields are C types pass to and return from C functions by value.
+// Records whose fields are C types pass to and return from C functions by
+// value, also as variadic arguments.
INCLUDE_OBJ "lib.o"
TYPE CShort = C_TYPE("short")
TYPE CFloat = C_TYPE("float")
@@ -34,10 +35,13 @@ EXTERN "C" EXTERN_FUNCTION make_pair(C_INT, C_INT) RETURNS Pair
EXTERN "C" EXTERN_FUNCTION make_vec2(C_DOUBLE, C_DOUBLE) RETURNS Vec2
EXTERN "C" EXTERN_FUNCTION make_mixed(C_CHAR, C_DOUBLE, C_INT) RETURNS Mixed
EXTERN "C" EXTERN_FUNCTION make_vec3f(CFloat, CFloat, CFloat) RETURNS Vec3f
+EXTERN "C" EXTERN_FUNCTION sum_vec2(C_INT, ...) RETURNS C_DOUBLE
+EXTERN "C" EXTERN_FUNCTION mixed_counts(C_INT, ...) RETURNS C_INT
DECLARE P : Pair
DECLARE U : Vec2
DECLARE V : Vec2
DECLARE M : Mixed
+DECLARE N : Mixed
DECLARE F : Vec3f
DECLARE W : Wide
P.A <- (C_INT)3
@@ -68,3 +72,6 @@ M <- make_mixed((C_CHAR)'Q', (C_DOUBLE)0.25, (C_INT)9)
OUTPUT M.Tag, " ", M.Value, " ", M.Count
F <- make_vec3f((CFloat)0.5, (CFloat)1.25, (CFloat)2)
OUTPUT F.X, " ", F.Y, " ", F.Z
+OUTPUT sum_vec2((C_INT)2, U, V)
+N <- make_mixed((C_CHAR)'R', (C_DOUBLE)1, (C_INT)5)
+OUTPUT mixed_counts((C_INT)2, M, N)
diff --git a/tests/iii/extern-struct-rejected/case.json b/tests/iii/extern-struct-rejected/case.json
index f83ef3e..cd7a3dd 100644
--- a/tests/iii/extern-struct-rejected/case.json
+++ b/tests/iii/extern-struct-rejected/case.json
@@ -11,6 +11,10 @@
{
"reason": "The\\ return\\ type\\ of\\ EXTERN_FUNCTION\\ atoi\\ uses\\ record\\ Person,\\ which\\ is\\ not\\ a\\ C\\ struct:\\ field\\ Name\\ of\\ Person\\ has\\ type\\ STRING",
"line": 10
+ },
+ {
+ "reason": "Variadic\\ argument\\ 2\\ to\\ printf\\ uses\\ record\\ Person,\\ which\\ is\\ not\\ a\\ C\\ struct:\\ field\\ Name\\ of\\ Person\\ has\\ type\\ STRING",
+ "line": 13
}
]
}
diff --git a/tests/iii/extern-struct-rejected/notes.md b/tests/iii/extern-struct-rejected/notes.md
index 3e733c1..650b420 100644
--- a/tests/iii/extern-struct-rejected/notes.md
+++ b/tests/iii/extern-struct-rejected/notes.md
@@ -1,5 +1,5 @@
# extern-struct-rejected
-Compiler extension: an EXTERN signature accepts a record, through a pointer type or by value, only as a C struct (every field a C type, a C struct or a pointer type to one). Person has a STRING field, so a pointer to it, Person as a by-value parameter and Person as a by-value result are all rejected and the field is named.
+Compiler extension: an EXTERN signature accepts a record, through a pointer type or by value, only as a C struct (every field a C type, a C struct or a pointer type to one). Person has a STRING field, so a pointer to it, Person as a by-value parameter, as a by-value result and as a by-value variadic argument are all rejected and the field is named.
-Require exit 1 and each cause on its EXTERN line.
+Require exit 1 and each cause on its EXTERN or call line.
diff --git a/tests/iii/extern-struct-rejected/program.cps b/tests/iii/extern-struct-rejected/program.cps
index 8fd1fd9..ced6fee 100644
--- a/tests/iii/extern-struct-rejected/program.cps
+++ b/tests/iii/extern-struct-rejected/program.cps
@@ -8,4 +8,6 @@ TYPE PPerson = ^Person
EXTERN "C" EXTERN_PROCEDURE puts(PPerson)
EXTERN "C" EXTERN_FUNCTION abs(Person) RETURNS C_INT
EXTERN "C" EXTERN_FUNCTION atoi(C_STRING) RETURNS Person
-OUTPUT 1
+EXTERN "C" EXTERN_FUNCTION printf(C_STRING, ...) RETURNS C_INT
+DECLARE Someone : Person
+OUTPUT printf((C_STRING)"%d", Someone)