aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 13:46:40 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 13:46:40 +0000
commite3cc6c585a15fe4478dd2312bf5d390896729a31 (patch)
tree1fbfbecd963d1d566c5d93d9c9cb3038b10fc89a /tests
parent3a6eb921b50e191468d29c233718657f9f85d68e (diff)
feat(codegen): return C-struct records by value
EXTERN_FUNCTION may now return a C-struct record by value, either through caller-supplied sret memory or as a coerced value stored back into the record, as clang arranges it. This removes the last by-value rejection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/i/extern-c-struct-value/expected.txt5
-rw-r--r--tests/i/extern-c-struct-value/lib.c10
-rw-r--r--tests/i/extern-c-struct-value/notes.md4
-rw-r--r--tests/i/extern-c-struct-value/program.cps15
-rw-r--r--tests/iii/extern-struct-rejected/case.json4
-rw-r--r--tests/iii/extern-struct-rejected/notes.md2
-rw-r--r--tests/iii/extern-struct-rejected/program.cps1
7 files changed, 36 insertions, 5 deletions
diff --git a/tests/i/extern-c-struct-value/expected.txt b/tests/i/extern-c-struct-value/expected.txt
index c1cc44b..19eef3c 100644
--- a/tests/i/extern-c-struct-value/expected.txt
+++ b/tests/i/extern-c-struct-value/expected.txt
@@ -3,3 +3,8 @@
13.000000
3.750000
1115
+3 4
+11
+1.500000 2.000000
+Q 0.250000 9
+0.500000 1.250000 2.000000
diff --git a/tests/i/extern-c-struct-value/lib.c b/tests/i/extern-c-struct-value/lib.c
index d6abe01..94afc3f 100644
--- a/tests/i/extern-c-struct-value/lib.c
+++ b/tests/i/extern-c-struct-value/lib.c
@@ -1,4 +1,4 @@
-/* Each struct is passed by value; the host C ABI decides how. */
+/* Each struct is passed or returned by value; the host C ABI decides how. */
struct Pair { int a, b; };
struct Vec2 { double x, y; };
struct Mixed { char tag; double value; int count; };
@@ -14,3 +14,11 @@ double vec3f_sum(struct Vec3f v) { return v.x + v.y + v.z; }
long long wide_after_five(int a, int b, int c, int d, int e, struct Wide w) {
return a + b + c + d + e + w.lo + w.hi;
}
+
+struct Pair make_pair(int a, int b) { struct Pair p = {a, b}; return p; }
+struct Vec2 make_vec2(double x, double y) { struct Vec2 v = {x, y}; return v; }
+struct Mixed make_mixed(char tag, double value, int count) {
+ struct Mixed m = {tag, value, count};
+ return m;
+}
+struct Vec3f make_vec3f(float x, float y, float z) { struct Vec3f v = {x, y, z}; return v; }
diff --git a/tests/i/extern-c-struct-value/notes.md b/tests/i/extern-c-struct-value/notes.md
index 912352a..a614d7b 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 by value like the matching C struct (C11 6.5.2.2p7: arguments are assigned to the parameters).
+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).
-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.
+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.
diff --git a/tests/i/extern-c-struct-value/program.cps b/tests/i/extern-c-struct-value/program.cps
index 9db6319..6b2cb8e 100644
--- a/tests/i/extern-c-struct-value/program.cps
+++ b/tests/i/extern-c-struct-value/program.cps
@@ -1,4 +1,4 @@
-// Records whose fields are C types pass to C functions by value.
+// Records whose fields are C types pass to and return from C functions by value.
INCLUDE_OBJ "lib.o"
TYPE CShort = C_TYPE("short")
TYPE CFloat = C_TYPE("float")
@@ -30,6 +30,10 @@ EXTERN "C" EXTERN_FUNCTION vec2_dot(Vec2, Vec2) RETURNS C_DOUBLE
EXTERN "C" EXTERN_FUNCTION mixed_score(CShort, Mixed, C_INT) RETURNS C_DOUBLE
EXTERN "C" EXTERN_FUNCTION vec3f_sum(Vec3f) RETURNS C_DOUBLE
EXTERN "C" EXTERN_FUNCTION wide_after_five(C_INT, C_INT, C_INT, C_INT, C_INT, Wide) RETURNS CLongLong
+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
DECLARE P : Pair
DECLARE U : Vec2
DECLARE V : Vec2
@@ -55,3 +59,12 @@ OUTPUT vec3f_sum(F)
W.Lo <- (CLongLong)100
W.Hi <- (CLongLong)1000
OUTPUT wide_after_five((C_INT)1, (C_INT)2, (C_INT)3, (C_INT)4, (C_INT)5, W)
+P <- make_pair((C_INT)3, (C_INT)4)
+OUTPUT P.A, " ", P.B
+OUTPUT pair_sum(make_pair((C_INT)5, (C_INT)6))
+U <- make_vec2((C_DOUBLE)1.5, (C_DOUBLE)2)
+OUTPUT U.X, " ", U.Y
+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
diff --git a/tests/iii/extern-struct-rejected/case.json b/tests/iii/extern-struct-rejected/case.json
index 09c7186..f83ef3e 100644
--- a/tests/iii/extern-struct-rejected/case.json
+++ b/tests/iii/extern-struct-rejected/case.json
@@ -7,6 +7,10 @@
{
"reason": "EXTERN\\ parameter\\ 1\\ of\\ abs\\ uses\\ record\\ Person,\\ which\\ is\\ not\\ a\\ C\\ struct:\\ field\\ Name\\ of\\ Person\\ has\\ type\\ STRING",
"line": 9
+ },
+ {
+ "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
}
]
}
diff --git a/tests/iii/extern-struct-rejected/notes.md b/tests/iii/extern-struct-rejected/notes.md
index 8d38d13..3e733c1 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 both a pointer to it and Person by value are 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 and Person as a by-value result are all rejected and the field is named.
Require exit 1 and each cause on its EXTERN line.
diff --git a/tests/iii/extern-struct-rejected/program.cps b/tests/iii/extern-struct-rejected/program.cps
index eec88a1..8fd1fd9 100644
--- a/tests/iii/extern-struct-rejected/program.cps
+++ b/tests/iii/extern-struct-rejected/program.cps
@@ -7,4 +7,5 @@ ENDTYPE
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