Safer C.
No rewrite required.

Drop into any codebase today. Your existing C compiles unchanged —
Prism adds defer, orelse, and zero-initialization on top.

version
v1.1.1
tests
5,400
stars
3
commits
585
deps
0
license
Apache 2.0
defer

Cleanup that can't be forgotten.

Bound to scope, not to every exit path. Runs in reverse order at scope end — whether you return early, fall through, or hit an error branch.

−18lines avg
3bug classes closed
Before
FILE *f = fopen(path, "r");
if (!f) return -1;

int r = parse(f);
if (r < 0) {
    fclose(f); // easy to forget
    return -1;
}

fclose(f);
return r;
After
FILE *f = fopen(path, "r")
    orelse return -1;
defer fclose(f);

return parse(f);
Before
char *buf = malloc(n);
if (buf == NULL) {
    log_error("OOM");
    return -1;
}
// ... 20 more lines
After
char *buf = malloc(n)
    orelse return -1;
orelse

Inline error handling without the noise.

Checks any falsy scalar — null pointer, zero integer, zero return code — inline. No nested if-blocks, no repeated cleanup calls, no diverging error paths. Works with return, break, continue, goto, or a fallback value.

−12lines avg
2bug classes closed
zero-init

No garbage values. Ever.

Every local variable is automatically zeroed at declaration — scalars, pointers, arrays, structs. The entire class of bugs from uninitialized reads is simply closed.

less undefined behavior
1bug class closed
Before
typedef struct {
    int width;
    int height;
    int flags; // garbage
} Config;

Config cfg; // uninitialized
if (cfg.flags & VERBOSE) {
    // undefined behavior
}
After
typedef struct {
    int width;
    int height;
    int flags;
} Config;

Config cfg; // zeroed automatically
if (cfg.flags & VERBOSE) {
    // always safe — flags == 0
}
C (compiles, silent leak)
int process(const char *path) {
    FILE *f = fopen(path, "r");
    if (!f) goto done;
    defer fclose(f); // skipped!
    do_work(f);
done:
    return 0; // f never closed
}
Prism (compile error)
int process(const char *path) {
    FILE *f = fopen(path, "r");
    if (!f) goto done;
    defer fclose(f);
    do_work(f);
done:
    // error: goto 'done' skips over
    // defer fclose(f)
    return 0;
}
safety

Control flow Prism catches.
C doesn't.

CFG analysis runs before any output is emitted. goto jumping over declarations, switch fallthrough skipping active defers, defer in setjmp/vfork/asm functions — all compile errors, not silent bugs.

0runtime overhead
all errors before emission
raw

Opt out. Precisely.

Zero-init has a cost on large buffers. raw skips initialization for a single variable without disabling anything globally.

per-variablegranularity
0global flags needed
// zeroed by default — 64k wasted memset
char buf[65536];
ssize_t n = read(fd, buf, sizeof(buf));

// raw: skip it — read fills it anyway
raw char buf[65536];
ssize_t n = read(fd, buf, sizeof(buf));

5,400 tests. Adversarially.

Not happy-path coverage. Edge cases, degenerate control flow, and patterns specifically designed to break a transpiler.

Prism is fully open source — Apache 2.0 licensed. Read every line, fork it, ship it. No strings.

Real-world codebases

OpenSSL, SQLite, Bash, Curl, GNU Coreutils, and Make — compiled unmodified through Prism. If it compiles and runs correctly there, it compiles and runs correctly everywhere.

Control flow nightmares

Nested loops with break and continue, switch fallthrough into deferred scopes, goto jumping over declarations, statement expressions inside defer bodies, computed goto with active defers. Every combination that could silently misfire.

Typedef disambiguation

Every typedef, enum constant, VLA tag, and parameter shadow — registered at all depths before a single byte of output is emitted. size_t x; and size_t * x; are not the same thing. Prism knows that.

Self-hosting

Prism compiles itself. Stage 1 and Stage 2 transpiled output is byte-for-byte identical — verified by CI on Linux x86_64, macOS x86_64/arm64, Linux arm64, and Linux riscv64. If it couldn't compile itself correctly, it couldn't ship.

GitHub

Built to spec.

Prism has a complete formal specification — every feature, every edge case, every invariant documented and cross-referenced against the test suite.

v1.1.1 · 5,400 tests · fully self-hosting · 14 invariants

Read the spec

Tokenizer, Pass 1 analysis, CFG verification, Pass 2 codegen, all language semantics.

Documentation

Getting started, CLI reference, feature guides, and architecture overview.

Continuous integration across Linux, macOS, and Windows — x86_64, arm64, and riscv64.

71%pass rate
71/100runs passing
5m 47savg run time
72fcf86fix 1 bug, stmt-expr false positive in defer shadow checkerrunning2 Apr 20:20 · 5m 12s
0ea1478fix 1 bug, emit_type_range stmt-expr bypass in struct bodiesrunning2 Apr 20:58 · 5m 15s
75ecedffix 3 bugs, dead const_td_is_array code, braceless switch derunning2 Apr 21:39 · 5m 6s
a850967fix 2 bugs, chained assignment orelse split-brain, block-scorunning2 Apr 23:44 · 5m 15s
8888ae3fix 2 bugs, typeof bit-field in bare orelse, array_size_is_vrunning3 Apr 07:51 · 5m 22s
c2372aafix 3 bugs, VLA param decay false positive, __auto_type orelrunning3 Apr 08:19 · 5m 13s
25cbe72fix 2 bugs, test harness cleanup: param multidim VLA sizeof running3 Apr 10:42 · 5m 28s
7ff985cfix golf test for GCC, rename all tests to objective names, running3 Apr 10:58 · 5m 30s
270b1fafix 3 bugs, case/default p1d_ctrl_pending leak bypasses CFG running3 Apr 15:57 · 5m 24s
0abd764fix 4 bugs, sizeof commutative VLA bypass (stack corruption)running3 Apr 17:06 · 5m 7s
acf026bfix 7 bugs, VM-type multi-decl split two-pass violation, ctrrunning3 Apr 19:58 · 5m 45s
a945663fix 3 bugs, C23 attr bypasses defer stmt-expr chain check twrunning3 Apr 21:01 · 5m 48s
a5d566efix 2 bugs, typeof parenthesized function name bypasses memsrunning3 Apr 21:21 · 4m 21s
690d625test count bumprunning3 Apr 21:24 · 5m 15s
cf274c1fix 1 bug, orelse inside ternary expression only caught by Prunning3 Apr 22:11 · 5m 49s
5a4243dfix 4 bugs, move ~925 lines of parsing infrastructure from prunning4 Apr 00:47 · 5m 31s
06c4b4dmove ~447 lines of pure analysis to parse.c, inline p1_add_srunning4 Apr 07:02 · 5m 41s
76c605bcleanuprunning4 Apr 08:05 · 5m 39s
7fa5c85fix 2 bugs, orelse inside ctrl-flow condition parens two-pasrunning4 Apr 10:53 · 5m 39s
c4c9311fix 5 bugs, raw*expr misclassified as pointer declaration strunning4 Apr 14:29 · 5m 27s
8abb51efix 3 bugs, noreturn attr arg poisoning, VLA deref paren adjrunning4 Apr 19:25 · 5m 31s
ea2c1c9gate 3 regression tests behind GNUC_ONLY for Windows CIrunning4 Apr 19:38 · 5m 34s
be1280dprep adding featuresrunning5 Apr 07:57 · 5m 20s
8fcca8afix 3 bugs, bare orelse typeof VM double-eval via typeof(RHSrunning5 Apr 11:40 · 5m 0s
0efa5edfix 2 bugs, C23 [[attr]] consumed into defer return-type typrunning5 Apr 13:51 · 5m 19s
2ac2200fix 2 bugs, C23 [[attr]] consumed into defer return-type typrunning5 Apr 14:44 · 4m 45s
40b6fbcfix 2 bugs, _Generic ternary colon confused with associationrunning5 Apr 15:29 · 5m 25s
6e926acfix 6 bugs, is_array_bracket_predecessor VLA hallucination orunning5 Apr 19:11 · 5m 24s
a694cb6fix 5 bugs, _Generic extraction discards trailing expressionrunning6 Apr 06:27 · 5m 4s
edae040fix 5 bugs, raw keyword leak in cast/sizeof/compound-literalrunning6 Apr 11:31 · 5m 37s
e32bdc2fix 5 bugs, MSVC diagnostic push/pop emitting nothing, f128 running6 Apr 12:57 · 5m 47s
452a220fix 5 bugs, ghost enum defer shadow escape in enclosing scoprunning6 Apr 13:39 · 5m 5s
3410f7dfix 5 bugs, typeof_unqual off-by-one const-stripping, _Generrunning6 Apr 14:29 · 5m 56s
c039946fix Windows CI: use has_zeroing() in typeof_unqual testrunning6 Apr 14:33 · 5m 41s
60fc35efix 5 bugs, _Generic inner-generic prefix drop, _Generic parrunning6 Apr 15:38 · 5m 43s
e51e08bfix 2 bugs, bracket orelse stmt-expr reentrancy state clobberunning6 Apr 16:24 · 5m 28s
e41533cfix 4 bugs, add formal spec + compliance testsrunning6 Apr 17:51 · 3m 5s
6dafccdfix 2 crashes, token_pool[0] OOB in K&R fallback, emit_deferrunning6 Apr 18:40 · 5m 17s
6ca00cefix 1 bug, stmt-expr paren-strip keyword leak; update SPEC.mrunning6 Apr 18:54 · 6m 15s
44ce49afix 2 bugs, typeof_var memset queue reentrancy clobber in strunning6 Apr 19:26 · 6m 10s
c13effdfix 6 bugs, typeof_var reentry test, RHS ctrl-flow duplicatirunning6 Apr 20:20 · 6m 11s
e966190fix 3 bugs, attr-raw VLA safety bypass, _Generic nested rewrrunning6 Apr 21:13 · 5m 50s
ae8a43ffix 4 bugs, backward goto nested-scope defer loop CFG bypassrunning6 Apr 22:06 · 6m 15s
c92930afix 1 bug, function type erasure in chained typedefs and typrunning6 Apr 22:14 · 5m 23s
f69d460fix 1 bug, _Generic member rewrite blind spots in defer bodirunning6 Apr 22:42 · 6m 3s
1841bdcdrop _generic re-write + spec updaterunning7 Apr 08:35 · 5m 40s
fe50fa9shrink P1FuncEntry and TypedefEntry from 40 to 32 bytes (2 prunning7 Apr 11:45 · 6m 7s
194f498fix emit_block_body + emit_deferred_range gaps; finish _Generunning7 Apr 18:23 · 5m 55s
58a4d7efix ObjC @interface/@implementation ivar zero-init; add regrrunning7 Apr 19:40 · 6m 6s
4d4e839fix 3 bugs, ObjC ivar zero-init, protocol/generics angle brarunning7 Apr 21:49 · 5m 56s
17d642aunify emit_block_body + emit_deferred_range into 2-mode emitrunning8 Apr 22:16 · 6m 5s
86a98d8fix 3 bugs, C23 [[attr]] skip in walk_back_skip_attrs on namrunning9 Apr 12:58 · 5m 31s
5299296fix 2 bugs, stmt-expr noise-skip for _Pragma/C23 attrs + typrunning9 Apr 16:03 · 6m 16s
17b40d0fix 2 bugs, C23 attr orelse FIFO queue steal + keyword leak,running9 Apr 18:11 · 6m 10s
63ab2b4fix 4 bugs, bracket raw leak, bare orelse raw leak, nested trunning9 Apr 21:45 · 6m 0s
e08fa63msvc test fixrunning9 Apr 21:55 · 6m 2s
86be39afix 6 bugs, constexpr+orelse/const-VLA/register-VLA Phase 1Drunning10 Apr 05:46 · 6m 5s
ed9fd38fix 3 bugs, defer body ctrl_state leak, typeof/bracket orelsrunning10 Apr 10:48 · 6m 5s
ed38b7efix 1 bug, extern decl false positive as nested function witrunning10 Apr 14:53 · 5m 45s
c2b7f68fix 1 bug, stmt-expr in ctrl condition label blindspotrunning10 Apr 15:25 · 6m 5s
b6f83b2fix 1 bug, typeof ctrl-flow keyword rejection gated on -fno-running10 Apr 16:02 · 5m 32s
2bf9e0dfix 5 bugs, defer body ctrl_state/orelse leak, BOFrame reentrunning10 Apr 17:43 · 6m 24s
c81cff2fix 3 bugs, volatile member memset UB, HashMap arena leak, crunning10 Apr 19:13 · 6m 16s
76c3ba1fix 2 bugs, C tag namespace collapse on volatile/VLA lookup,running10 Apr 19:47 · 6m 23s
6deeb34fix windows testrunning10 Apr 19:52 · 5m 58s
5c3657bfix 1 bug, paren-wrapped orelse smuggling bypasses Phase 1F running10 Apr 20:09 · 6m 17s
3cc88ebfix 2 bugs, Phase 1D split predicate blind to current-decl orunning10 Apr 20:42 · 6m 21s
018a0a0fix 1 bug, miscompilation via lexical noreturn shadowingrunning10 Apr 20:57 · 6m 1s
968d8d6windows test fixrunning10 Apr 21:00 · 6m 12s
320fe44fix 3 bugs, attribute orelse leak, raw struct body leak, rawrunning11 Apr 10:21 · 5m 33s
490a8c5stmt-expr dispatch in flat-emit loops + _Alignas SUE probe srunning11 Apr 15:16 · 5m 40s
76a1612fix 2 bugs, attribute-stealing struct tag scanner, C11 §6.2.running11 Apr 16:44 · 5m 46s
9e71bc1fix 4 bugs, param orelse funcdef bypass, block-scope typedefrunning11 Apr 18:02 · 5m 34s
61b5fb3fix 2 bugs, ILP32 keyword shift UB, braceless body compound running11 Apr 19:35 · 5m 39s
379c242fix 3 bugs, ILP32 keyword shift UB, braceless compound literrunning11 Apr 19:53 · 5m 40s
f439990fix 3 bugs, typeof funcptr attribute blindness, missing C23/running11 Apr 21:22 · 5m 41s
bf2dedafix windows zero init testrunning11 Apr 21:40 · 6m 19s
5661e79fix 2 bugs, __extension__ for-init zeroinit bypass, __extensrunning12 Apr 09:12 · 6m 22s
1081ea0fix 4 bugs, defer shadow false positive on block-local declsrunning12 Apr 14:20 · 5m 50s
f7caf27fix 3 bugs, skip_to_semicolon defer validator scope escape, running12 Apr 16:23 · 5m 56s
a45666bfix 3 bugs, enum defer shadow false positive, walk_balanced running12 Apr 17:37 · 6m 27s
f7caf27fix 3 bugs, skip_to_semicolon defer validator scope escape, running12 Apr 17:44 · 5m 51s
864197bfix 3 bugs, enum defer shadow false positive, walk_balanced running12 Apr 17:49 · 5m 43s
792ec8bfix 5 bugs, chained orelse defer control-flow leak, typeof orunning12 Apr 21:16 · 7m 18s
d0b7f55implement auto staticrunning13 Apr 18:39 · 6m 33s
89ef391fix 1 bug, auto unreachable noreturn attribute placed post frunning13 Apr 19:14 · 6m 31s
d60d16afix windows posix shimrunning13 Apr 19:27 · 6m 42s
bc042f2fix windows testsrunning13 Apr 19:47 · 5m 51s
37620c5fix 3 vulns, union = {0} padding infoleak, compound literal running13 Apr 22:16 · 6m 41s
16bdb29fix windows testrunning13 Apr 22:27 · 6m 36s
6c12a53fix 3 bugs, -fno-orelse defer-in-paren bypass, widen ann to running14 Apr 12:21 · 6m 31s
17013d6fix 6 bugs, attr noise blindspot in VLA predecessor, _Atomicrunning14 Apr 14:27 · 5m 48s
e11bd95fix 4 bugs, VM func ptr orelse double-eval, #define block corunning15 Apr 11:33 · 6m 28s
fe20798fix 3 bugs, #define comment strip UAF, realloc double-free, running15 Apr 12:09 · 6m 8s
871c20ffix 2 bugs, _Generic defer body missing SCOPE_GENERIC, stalerunning16 Apr 07:01 · 6m 41s
345e5d2fix 2 bugs, stmt-expr for-init defer shadow desync, defer kerunning16 Apr 13:27 · 6m 29s
387b1c6fix 2 bugs, __label__ ctrl_pending leak in Phase 1D, Pass 0 running16 Apr 20:25 · 6m 44s
253266efix 2 bugs, ret_counter desync between __prism_ret_N decl anrunning16 Apr 20:26 · 6m 27s
98423f6fix 3 bugs, typeof VLA with paren/call in dim skipped zeroinrunning16 Apr 21:46 · 6m 54s
7234b51fix macos arm64 test flake, replace fork()+execvp()+system(3running16 Apr 22:31 · 2m 1s
Linuxx86_64success38s
Linuxarm64success35s
Linuxriscv64--
macOSx86_64success31s
macOSarm64success20s
Windowsx86_64success49s

Prism is built and maintained by one person, in the open, for free.

If it saves you time or makes your C safer, sponsoring is how you keep that going. Every sponsor directly funds time on new features, bug fixes, and keeping the test suite green.

Available for consulting work across design, branding, and engineering.

Compiler work, systems programming, C codebase hardening, dev tooling, or a visual identity for your project — send an email.

dawn@dawn.day

Serious inquiries only.