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