kit

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

commit 6a97a84ce5ab1c6b9b2c19618cb7f535fe25a725
parent 4141ece5135aed61fe3674533de390d8202a8340
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 19 May 2026 16:01:10 -0700

C11 BUGS add red tests

Diffstat:
Mdoc/BUGS.md | 37++++++++++++++++++++++++++++---------
Atest/parse/cases/6_5_15_01_conditional_comma_in_middle.c | 17+++++++++++++++++
Atest/parse/cases/6_5_15_01_conditional_comma_in_middle.expected | 1+
Atest/parse/cases/6_5_2_1_01_subscript_conditional_zero_branch.c | 16++++++++++++++++
Atest/parse/cases/6_5_2_1_01_subscript_conditional_zero_branch.expected | 1+
Atest/parse/cases/6_7_6_21_field_ptr_to_func_returning_funcptr.c | 30++++++++++++++++++++++++++++++
Atest/parse/cases/6_7_6_21_field_ptr_to_func_returning_funcptr.expected | 1+
Atest/parse/cases/6_7_9_31_static_init_const_float_expr.c | 16++++++++++++++++
Atest/parse/cases/6_7_9_31_static_init_const_float_expr.expected | 1+
9 files changed, 111 insertions(+), 9 deletions(-)

diff --git a/doc/BUGS.md b/doc/BUGS.md @@ -12,6 +12,10 @@ Format as: - [x] function declarator with an inline function-pointer return type (no typedef): `6_7_6_20_func_returning_funcptr_no_typedef` - [x] static initializer accepts unary `-` on a floating constant: `6_7_9_30_static_init_neg_float` - [x] `#warning` preprocessing directive (non-fatal, parsing continues): `6_10_warning_directive` +- [ ] static initializer accepts a binary constant expression on floating constants (`1.0f/2.2f`): `6_7_9_31_static_init_const_float_expr` +- [ ] conditional operator allows a comma expression in its middle operand (`a ? b, c : d`): `6_5_15_01_conditional_comma_in_middle` +- [ ] subscript accepts a conditional whose constant arm is `0` without treating it as a null pointer: `6_5_2_1_01_subscript_conditional_zero_branch` +- [ ] struct field declarator `RETTY (*(*name)(P))(IP)` (pointer-to-function-returning-function-pointer; sqlite VFS `xDlSym`): `6_7_6_21_field_ptr_to_func_returning_funcptr` Known bugs caught by other harnesses @@ -19,17 +23,32 @@ Known bugs caught by other harnesses - [x] clang-emitted Mach-O `.o` rejected by `cfree ld` reader (`read_macho: non-extern reloc not supported`). Root cause: clang emits section-relative relocations (`r_extern == 0`) in `__LD,__compact_unwind` (and DWARF/EH sections); cfree's IR only modelled symbol-relative relocs. Fixed in `src/obj/macho_read.c` by lazily synthesizing one `.Lcfree.macho_secstart.<idx>` local symbol per referenced section and re-expressing the reloc as `target = sec_start_sym, addend = inplace_value - section.addr_in_obj`. The linker then resolves it to `target.vaddr + addend`, matching the original referent. Verified by linking `xcrun clang -c hello.c -o hello.o` output through `cfree ld -lSystem` and running. -- [ ] segfault compiling `lua-5.4.7/src/lparser.c` (no diagnostic, no minimal reduction yet): no red test yet +- [ ] Mach-O `link_macho: coalesce mismatch on __TEXT,__text (flags/zerofill)` when linking certain cfree-emitted relocatable objects. Reproduces with cfree-compiled `tmp/projects/stb_sprintf.h` (driver `tmp/refresh/use_stb_sprintf.c`) and `tmp/projects/cJSON/cJSON.c`; the trivial `int main(){return 0;}` + hosted shim still links, and `tmp/refresh/use_jsmn.c` links and runs end-to-end. The differentiator looks like a section-flag fan-out where an `__TEXT,__text` MSec gets emitted next to a `zerofill`-flagged MSec under the same `(segname, sectname)`, which trips the Phase A/B mismatch check in `src/link/link_macho.c`. No reduction yet: + + ```sh + # Compile is fine: + build/cfree cc -target aarch64-darwin -isystem rt/include/libc -isystem rt/include \ + -c tmp/refresh/use_stb_sprintf.c -o /tmp/stb.o + # Link fails: + SDK="$(xcrun --show-sdk-path)" + build/cfree cc -target aarch64-darwin -e main -L "$SDK/usr/lib" \ + -o /tmp/stb.exe /tmp/stb.o build/cfree_hosted/macos.o -lSystem + # → fatal: link_macho: coalesce mismatch on __TEXT,__text (flags/zerofill) + ``` + +- [ ] aarch64 call lowering rejects "INDIRECT arg storage kind 3". Reproduces compiling `cJSON_Utils.c:845`, which passes a sized aggregate by value to a function. The AAPCS64 classifier picks INDIRECT but the call emitter has no path for the source-storage shape it sees there. No minimal repro yet: + + ```sh + build/cfree cc -target aarch64-darwin -isystem rt/include/libc -isystem rt/include \ + -c tmp/projects/cJSON/cJSON_Utils.c -o /tmp/u.o + # → fatal: aarch64 call: INDIRECT arg storage kind 3 unsupported + ``` + +- [ ] silent SIGSEGV with no diagnostic when compiling much of lua-5.4.7. After B3 was fixed, 18 lua TUs now crash cfree (exit 139): `lapi, lcode, ldebug, ldo, ldump, lfunc, lgc, llex, lmem, lobject, lparser, lstate, lstring, ltable, ltm, luac, lundump, lvm, lzio`. The other 14 (`lauxlib, lbaselib, lcorolib, lctype, ldblib, linit, liolib, lmathlib, loadlib, lopcodes, loslib, ltablib, lua, lutf8lib`) compile cleanly. No minimal reduction yet, so no red test: ```sh - # Source lives at tmp/projects/lua/src/lparser.c (lua-5.4.7). - # First clear B3 (parenthesized declarator names in lua.h / lauxlib.h) - # so the parser gets far enough to crash: - sed -i.bak -E 's/^([A-Z_]+API[[:space:]][^*]+\*?)[[:space:]]*\(([a-zA-Z0-9_]+)\)[[:space:]]*\(/\1 \2(/' \ - tmp/projects/lua/src/lua.h tmp/projects/lua/src/lauxlib.h build/cfree cc -target aarch64-darwin \ - -isystem rt/include/libc -isystem rt/include -DLUA_USE_POSIX \ - -c tmp/projects/lua/src/lparser.c -o tmp/lparser.o + -isystem rt/include/libc -isystem rt/include \ + -c tmp/projects/lua/src/lparser.c -o /tmp/lparser.o # → Segmentation fault: 11 (exit 139, no diagnostic) - # Needs reduction before it becomes a parse-case-sized repro. ``` diff --git a/test/parse/cases/6_5_15_01_conditional_comma_in_middle.c b/test/parse/cases/6_5_15_01_conditional_comma_in_middle.c @@ -0,0 +1,17 @@ +/* §6.5.15 -- the middle operand of the conditional operator is a full + * `expression`, which by §6.5.17 includes comma-separated sequences. + * `a ? b, c : d` parses as `a ? (b, c) : d`. cfree's expression parser + * bottoms out at assignment-expression for the middle operand and trips + * on the comma with "expected ':' in ternary". Hits stb_image_write.h + * via `stbiw__sbfree(a) ((a) ? STBIW_FREE(raw(a)),0 : 0)`. */ +static int sink; + +static int f(int side_effect, int result) { + sink = side_effect; + return result; +} + +int test_main(void) { + int x = 1 ? f(7, 0), 42 : 99; + return (sink == 7) ? x : 0; +} diff --git a/test/parse/cases/6_5_15_01_conditional_comma_in_middle.expected b/test/parse/cases/6_5_15_01_conditional_comma_in_middle.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/6_5_2_1_01_subscript_conditional_zero_branch.c b/test/parse/cases/6_5_2_1_01_subscript_conditional_zero_branch.c @@ -0,0 +1,16 @@ +/* §6.5.2.1 -- E1[E2] requires one pointer-to-object and one integer + * operand. When E2 is a conditional expression whose two arms are an + * integer constant `0` and a non-constant integer, cfree (after the + * usual arithmetic conversions on `?:`) appears to treat the constant + * `0` arm as a null-pointer-constant, unifies the `?:` result as a + * pointer, then rejects `ptr[ptr]`. Hits lua-5.4.7 lstrlib.c at + * buff[islittle ? 0 : size - 1] = ... + * which the diagnostic flags as "invalid subscript: needs one pointer + * and one integer". */ +int test_main(void) { + char buff[4] = { 0 }; + int islittle = 1; + int size = 4; + buff[islittle ? 0 : size - 1] = 42; /* writes buff[0] = 42 */ + return buff[0]; +} diff --git a/test/parse/cases/6_5_2_1_01_subscript_conditional_zero_branch.expected b/test/parse/cases/6_5_2_1_01_subscript_conditional_zero_branch.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/6_7_6_21_field_ptr_to_func_returning_funcptr.c b/test/parse/cases/6_7_6_21_field_ptr_to_func_returning_funcptr.c @@ -0,0 +1,30 @@ +/* §6.7.6 -- 6_7_6_20 covered the function-declarator form + * `RETTY (*name(P))(IP)`. SQLite VFS's `xDlSym` uses the *struct-field* + * form, where the outer declarator is itself a pointer: + * + * void (*(*xDlSym)(void*, const char *))(void); + * + * That is: a pointer-to-function-taking-(void*,const char*)-returning- + * pointer-to-function-taking-(void)-returning-void. cfree's declarator + * parser still rejects this shape with "expected declarator name" at + * the inner `(*xDlSym)` — typedeffing the inner return type works + * around it, but the inline form is what shows up in sqlite3.h:1503 / + * sqlite3.c:1822. */ +static void inner(void) {} + +static void (*pick(void *p, const char *n))(void) { + (void)p; (void)n; + return inner; +} + +struct vfs { + void (*(*xDlSym)(void *, const char *))(void); +}; + +int test_main(void) { + struct vfs v; + v.xDlSym = pick; + void (*fn)(void) = v.xDlSym((void *)0, "x"); + fn(); + return 42; +} diff --git a/test/parse/cases/6_7_6_21_field_ptr_to_func_returning_funcptr.expected b/test/parse/cases/6_7_6_21_field_ptr_to_func_returning_funcptr.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/6_7_9_31_static_init_const_float_expr.c b/test/parse/cases/6_7_9_31_static_init_const_float_expr.c @@ -0,0 +1,16 @@ +/* §6.7.9 / §6.6 -- a static-storage object of arithmetic type takes an + * arithmetic constant expression as its initializer. A *binary* operator + * applied to two floating constants is such an expression. cfree's + * file-scope-init path only accepts a bare TOK_FLT / TOK_NUM (optionally + * with the unary `-` after 6_7_9_30 was fixed), so `1.0f/2.2f` aborts the + * declaration mid-list with "expected ';' after global declaration". + * Hits stb_image.h at the `static float stbi__h2l_gamma_i=1.0f/2.2f, + * stbi__h2l_scale_i=1.0f;` declaration. */ +static float a = 1.0f / 2.2f; +static float b = 1.0f; + +int test_main(void) { + /* 1.0/2.2 ≈ 0.4545; (int)((a + b) * 10) should be 14. Add 28 so the + * exit code is the canonical 42. */ + return (int)((a + b) * 10.0f) + 28; +} diff --git a/test/parse/cases/6_7_9_31_static_init_const_float_expr.expected b/test/parse/cases/6_7_9_31_static_init_const_float_expr.expected @@ -0,0 +1 @@ +42