Releases
Prism release history. All releases are available on GitHub Releases.
v1.1.1 current
Prism 1.1.1 is here — this release is all bug fixes: 27 bugs across 9 commits, including memory-safety vulnerabilities in the preprocessor pipeline, multiple zero-init bypass vectors, defer/orelse interaction breakage, and emitter-level codegen drift.
Security
#definecomment-strip use-after-free —collect_source_definesretained pointers into a buffer that was reallocated mid-scan when a#defineline crossed the SWAR padding boundary; subsequent dereference read freed memory (fe20798)reallocdouble-free in source-defines arena — themodifiedflag was reused across iterations, causing the same buffer to be re-realloc'd then freed twice on certain#definepatterns (fe20798)typeof(int[f()])/typeof(int[(n)])skipped zero-init —is_typeof_func_type's top-level walk did not skip balanced[...]groups, so any(inside an array-dimension expression was misread as a function parameter list, classifying the typeof as a function type and suppressing memset; declared local VLA was left indeterminate,x[0]read uninitialized stack memory (info leak) (98423f6)- Attribute-noise blindspot in VLA predecessor —
is_array_bracket_predecessordid not walk past GNU__attribute__((...))and C23[[...]]between a SUE keyword and a tag name, sotypeof(struct __attribute__((packed)) Tag [n])failed VLA detection and skipped zero-init (17013d6) _Atomic(...)inner-type VLA scan missing —_Atomic(int[n])and_Atomic(VLA_typedef)were not flagged as VLA, falling through to= {0}(compile error) or no init (info leak);parse_type_specifiernow scans_Atomic(...)contents identically totypeof(...)(17013d6)typeof(struct __attribute__((...)) S)zero-init skip — anonymous struct with attribute noise betweenstructkeyword and body was not detected as aggregate, dropping the memset (e11bd95)- VM func-ptr
orelsedouble-evaluation —typeof(RHS)in the bare-orelse temp pattern evaluated the RHS twice when the result type was variably modified (C11 §6.7.2.4p2); a function pointer with VLA-array return type triggered the duplication, double-callingf()and double-reading volatile sources (e11bd95) -fno-orelsedefer-in-paren safety bypass — withF_ORELSEdisabled,defer (return);and similar paren-wrapped defer bodies bypassed Phase 1F validation entirely; theorelseparen-recursion scanner now also runs when onlyF_DEFERis enabled (6c12a53)defer-bodyorelseemitter overshoot —emit_bare_orelse_implandemit_deferred_orelseconsumed the trailing;and advanced past it without respecting the deferred-rangeendboundary, spilling the next user statement (and the function body's closing}) into the emitted defer body — invalid C output (253266e)ret_counterdesync in defer-wrapped return —emit_return_bodyreadctx->ret_countertwice (once for__prism_ret_<N> = (...), once forreturn __prism_ret_<N>;) withemit_all_defers()between them; the defer body's__prism_oe_Kallocations bumped the shared counter, emitting areturn __prism_ret_<M>referring to an undeclared identifier (253266e)
Bug Fixes
noreturn: K&R-style funcptr param scan misclassifiedvoid (*cb)(int, int);as a forward declaration; precise inner-paren walk discriminates parameter type list from declarator (17013d6)noreturn: forward-declaration scan failed when the noreturn attribute appeared between qualifier and identifier; func-proto map now records both branches (17013d6)- stmt-expr inside control-flow paren — a
({…})insideif(…)/while(…)parens corrupted Phase 1D'sat_stmt_starttracking on the body's first token, dropping declaration annotation and zero-init (17013d6) defer enum bodyfalse shadow — Phase 1D's enum-body scanner falsely flagged inner-scope enum constants as shadowing defer-captured names when no control-flow exit was reachable while the shadow was live (e11bd95)#defineblock-comment leak in source-defines collection — multi-line/* ... */comments containing#definelines silently scraped_FILE_OFFSET_BITSand similar macros into the output, mutating the compiled program's ABI (e11bd95)_Genericdefer body missingSCOPE_GENERIC— defer bodies inside_Genericassociations did not push the generic scope, causing:separators in nested associations to be mis-identified as labels (871c20f)- Stale
last_emittedin 5 orelse scan-context checks —last_emittedfrom the prior emission leaked into orelse's "is keyword" disambiguation, producing both false rejections and false acceptances depending on the prior token (871c20f) - stmt-expr in for-init defer-shadow desync —
for ( ({ defer ...; }) ; ; )produced incorrect shadow-vs-capture decisions because Phase 1D's shadow scanner used the wrong scope-id at the stmt-expr boundary (345e5d2) deferkeyword leak in expression context —(s.defer),arr[defer], and other expression positions wheredeferis a member/subscript-context identifier emitted thedefertoken verbatim to the backend, producing a compile error (345e5d2)__label__ctrl_pendingleak in Phase 1D — GNU__label__ L1, L2;declarations did not clearp1d_ctrl_pending, causing a subsequent declaration to be erroneously bounded to a non-existent braceless control body (387b1c6)- Pass 0 taint variable-name false positive — a local variable named
vfork(or any taint-tagged identifier) inherited the global TT_SPECIAL_FN tag, falsely tainting the function and rejecting defer; member-access predecessor check added (387b1c6) - Wrapper-of-
exitfalsely tainted as vfork —wrapper_taint[]propagated bareTT_NORETURN_FNfrom a callee onto the wrapper's body without mirroring the direct-body scan's vfork-only rule, hard-rejecting any defer in functions calling a user wrapper ofexit/abort/_Exit/thrd_exit/quick_exitwith a misleading "vfork()" error (98423f6) - Braceless defer body declaration with
orelseinitializer emitted invalid__typeof__(int t) __prism_oe_N = ...— Phase 1D walks braceless defer bodies in the!at_stmt_startbranch, missing theP1_IS_DECLannotation; Phase 1F now rejects with a clear message directing the user atdefer { ... }(98423f6) - Pass 1
annfield flag space exhausted at 8 bits — widened touint16_tand addedP1_REJECTEDfor explicit rejection tracking (6c12a53)
Stats
- 27 bugs fixed (17 bug fixes, 10 security vulnerabilities)
- +166 new tests, total: 5,400 tests passing (alpine x86_64 / linux CI; 5,361 on darwin/clang)
- +1,663 lines of new test coverage
- 14 files changed, +2,131 insertions, −339 deletions
- Self-host: stage1 == stage2 byte-identical
v1.1.0
Prism 1.1.0 is here — this release introduces the auto-static progressive optimization and bug fixes.
New Features
- Auto-static —
constarrays with literal initializers are automatically promoted from stack tostaticstorage, eliminating hiddenmemcpyon every function call. Fires conservatively: block-scopeconstarrays only, every initializer token must be a literal or enum constant, novolatile/static/extern/register/constexpr/_Thread_local, no VLA dimensions, noorelse, no attributes on the declarator. Pointer arrays requireconston the array itself (const int * const arr[3]). Opt-out:-fno-auto-static(d0b7f55)
Security
- Union
= {0}padding infoleak —= {0}only initializes the first named member of a union (C11 §6.7.9p17); §6.7.9p21 implicit zeroing applies only to aggregates (C11 §6.2.5p21: arrays and structs, NOT unions); GCC-15 empirically leaves remaining bytes indeterminate — unions now route to__builtin_memsetunconditionally, withis_uniontracked through the full type system including_Atomic(union)and typedef chains viaTDF_UNION - Compound literal dangling pointer via
top_is_callheuristic — function-call exclusion in compound literal detection allowedidentity_ptr(&(struct Config){.mode=1})to use if/else path, scoping the compound literal to the else block (C11 §6.5.2.5p5) — if the function returns the CL address, the pointer dangles; token-level analysis cannot determine escape behavior; removed heuristic entirely, ternary path now unconditional at all parenthesis depths registerunion zeroinit bypass —register union U u;fell through to= {0}(memset blocked by!has_register), only zeroing the first member; now rejected with a hard error in both Phase 1D and Pass 2, sinceregisterforbids address-taking (ISO C11 §6.7.1p6) making memset impossible
Bug Fixes
noreturn: attribute scanner only searched forward from attribute position — post-declarator forms likevoid die(void) __attribute__((noreturn));invisible because scan hit;before findingident(; added backward scan from attribute origin through token pool (89ef391)zeroinit:_Atomic(union U)and_Atomic(UnionTypedef)missingis_unionflag propagation —_Atomic(type)handler inparse_type_specifiernow setsis_unionfor both direct SUE and typedef inner typeszeroinit: Phase 1D for-init union rejection missing —for(union U u; ...)passed Phase 1D, caught only by Pass 2 defense-in-depth; two-pass invariant violation fixedzeroinit: Phase 1D const-VLAwould_memsetmissing union —const union U u;requiring memset not caught by const rejection check- Windows POSIX shim and test compatibility fixes (d60d16a, bc042f2)
Tests
- Auto-unreachable test suite — +183 tests validating
__builtin_unreachable()injection after noreturn function calls across all attribute forms, chained calls, and edge cases (89ef391)
Stats
- 7 issues fixed (4 bugs, 3 security vulnerabilities)
- +112 new test functions, +319 new tests, total: 5,234 tests passing
- +2,337 lines of new test coverage
- 10 files changed, +2,651 insertions, −102 deletions
v1.0.9
Prism 1.0.9 is here, a focused correctness and security release — 17 bugs were fixed across defer validation, orelse transformation, zero-initialization, and the typedef table, with particular attention to chain-aware scanning in defer bodies and orelse side-effect validation.
Security
- Defer validator scope escape —
skip_to_semicolonin Phase 1F'svalidate_defer_statementhad no scope boundary check; a missing;inside adefer { ... }block caused the scanner to walk past}into unrelated code, either producing spurious errors on valid functions or silently missing real control-flow violations (f7caf27) - Chained orelse defer control-flow leak —
defer_scan_orelse_in_groupandvalidate_defer_statement's inline orelse scanner both had prematurebreakafter the firstorelse; chained(expr orelse expr orelse return)in defer bodies letreturn/goto/break/continuebypass Phase 1F entirely, breaking LIFO unwinding guarantees (792ec8b)
Bug Fixes
defer: false "shadows a name captured by defer" errors for variables declared at the top level of defer bodies —defer_body_refs_nameused wrong block-depth threshold (1081ea0)defer:walk_balancedexpression-context enum bodies (sizeof(enum { X = 1 })) never registered defer shadow entries — silent rebinding at exit points (864197b)defer:walk_balanced_orelsearray-dimension enum bodies had the same shadow-tracking gap aswalk_balanced(864197b)defer: enum defer shadow check in Phase 1D issued hard error even when enclosing block has no control-flow exits — false positive on valid code (864197b)defer: case/default label scanner invalidate_defer_statementescaped block boundaries on complex case expressions (f7caf27)defer: O(N×M) quadratic blowup indefer_body_refs_namecaused apparent hang on large generated/macro-heavy files — replaced with single-pass O(M) capture set + O(1) per-name lookups (792ec8b)orelse:typeof(... orelse ...)verbatim keyword leak in 9 emit paths —try_typeof_orelsehook missing fromemit_range_ex,emit_expr_to_stop,emit_raw_verbatim_to_semicolon,emit_orelse_fallback_value,emit_expr_to_semicolon, andemit_bare_orelse_implinner loops (792ec8b)orelse: chained typeof orelse side-effect validation gap — Phase 1Dbreakafter firstorelseonly checked first LHS, intermediate LHS ranges intypeof(a orelse b orelse c)unchecked for side effects (792ec8b)zeroinit:__extension__-prefixedfor-init declarations bypassed zero-initialization —p1_scan_init_shadowsdidn't skip__extension__/TT_INLINEprefix tokens (5661e79)zeroinit:P1_IS_DECLannotation placed on__extension__prefix instead of actual type keyword — Pass 2 scan past storage/inline tokens never found annotation (5661e79)zeroinit: declarations after complex case labels (case (int)'A':,case ENUM | FLAG:) missed zeroinit —:handler only recognized identifier/number predecessors (1081ea0)zeroinit: bracelesswhile/doloop body zeroinit gated onFEAT(F_DEFER)only — with-fno-defer, brace wrapping never applied (1081ea0)zeroinit:any_would_memsettwo-pass invariant violation — Phase 1D split predicate missingis_typedefin_Atomicaggregate check, disagreeing with Pass 2 (1081ea0)- Inner-scope struct tag redefinitions not registered when body lacked VLA/volatile members —
tag_lookupreturned stale outer-scope flags, causing false CFG errors or incorrect memset (f7caf27) - Struct tag registration blocked by same-name typedefs —
parse_typedef_declarationgated on!is_known_typedef(tag), losing volatile/VLA member tracking for the commontypedef struct Foo { ... } Foopattern (792ec8b)
Stats
- 17 bugs fixed, 2 security vulnerabilities patched
- +8 new test functions, +56 new tests, total: 4,915 tests passing
- +728 lines of new test coverage
- 8 files changed, +1,031 insertions, −142 deletions
v1.0.8
Prism 1.0.8 continues the correctness and security focus, closing 20 bugs across the transpiler with particular attention to keyword table coverage, namespace isolation, statement-expression dispatch completeness, and braceless control flow scope tracking.
Security
- ILP32 keyword shift UB —
KW_MARKERused(uintptr_t)flags << 32which is undefined behavior on 32-bit platforms whereuintptr_tis 32 bits, corrupting all keyword tag/flag classification and silently breaking every keyword-dependent analysis (61b5fb3) - Braceless compound literal scope underflow — compound literal braces
(struct S){...}inside braceless control flow didn't decrementctrl_state.brace_depthproperly, leakingctrl_state.pendingto the next statement and popping defer shadows below the current scope (61b5fb3) - Braceless shadow scope leak —
p1d_probe_declarationregistered typedef shadows using the enclosing block's scope close, so braceless body declarations likeif (c) int MyTypedef;permanently poisoned the name for the entire enclosing scope, blinding the CFG verifier and skipping zero-init (379c242) walk_balancedbracket stmt-expr bypass — the bracket[...]flat-emit loop had nois_stmt_expr_opencheck, so({...})inside array subscripts in balanced groups bypassed zero-init, defer cleanup, and all keyword processing (490a8c5)emit_noise_between_rawsstmt-expr bypass — flatemit_tokloop lacked stmt-expr dispatch, allowing({...})inside attribute noise between raw declarators to bypass the full processing engine (490a8c5)process_declaratorsraw-bail stmt-expr bypass — the raw declaration verbatim-emit loop used flat token emission without stmt-expr detection, allowing zero-init and defer bypass inside statement expressions (490a8c5)_AlignasSUE probe skip in defer —EMIT_DEFER_BODY's SUE body probe didn't skip parenthesized arguments of qualifiers, so_Alignas(16) struct S { int x; };in a defer body caused= 0to be injected into struct field definitions (490a8c5)- Attribute-stealing struct tag scanner —
skip_noisewas missing from the struct tag extraction loop, sostruct __attribute__((aligned(8))) Tag {registeredalignedinstead ofTagas the struct name — corrupting VLA/volatile member tracking (76a1612) - C11 §6.2.3 namespace collapse —
typedef_add_entryduplicate check,typedef_lookup, andparse_type_specifier's typeof handler collapsed C's tag namespace with ordinary identifiers, silently dropping struct tag entries and bypassing zero-init/volatile tracking (76a1612) - Param orelse funcdef bypass —
p1d_reject_proto_param_orelseonly checked for;after)(prototypes), letting function definitions with{escape entirely —void f(int buf[hw_flag orelse 128]) { }produced a ternary with volatile double-read UB (9e71bc1) - Asm specifier evasion — GCC
__asm__("symbol_name")renaming specifiers after)were not skipped byskip_noise, causing the;/{detection to fail and the entire bracket-orelse parameter scan to be silently skipped (9e71bc1) - Block-scope typedef rettype gate —
p1_func_proto_mapatbrace_depth>0missed typedef-named return types, sotypeof(f)inside a function body triggered spurious memset on a function type — writing to .text segment (SIGSEGV) (9e71bc1) - Nested funcptr param scan — bracket-orelse parameter scan was flat (no recursion into nested
()groups), so orelse inside function-pointer parameter brackets was never checked, enabling the same volatile double-eval UB (9e71bc1) typeoffuncptr attribute blindness —is_typeof_func_typeusedtok_next(fs)withoutskip_noise, missing__attribute__/[[...]]between(and*— function pointer type misclassified as function type, skipping memset → write to .text → SIGSEGV (f439990)- Missing C23/GCC type keywords — 14 float types (
__float128,_Float16,_Float32,_Float32x,_Float64,_Float64x,_Float128x,_Decimal32,_Decimal64,_Decimal128, etc.) and 2 typeof_unqual variants (__typeof_unqual__,__typeof_unqual) absent from keyword table — treated as plain identifiers, bypassing zero-init; also fixedis_unquallength check (== 13→>= 13) which broke qualifier stripping for 15/17-char variants (f439990)
Bug Fixes
orelse:orelseinside GNU__attribute__((...))or C23[[...]]arguments was invisible to Phase 1D, leaking the raw keyword to the backend compiler (320fe44)raw:emit_type_rangehad notry_strip_rawin its finalemit_tokfallthrough, sorawinside struct/union bodies at brace depth > 0 leaked verbatim to C output (320fe44)raw:emit_token_rangeused bareOUT_TOK(t)withouttry_strip_raw, soraw int f() { defer ...; return 42; }producedraw int __prism_ret_0 = (42);(320fe44)orelse:reject_orelse_side_effectsfired unconditionally on bare orelse RHS with LHS indirection, but function return types are never variably-modified (C11 §6.7.6.3p1) — false positive on valid code (76a1612)defer:emit_statements':label handler was gated onmode != EMIT_DEFER_BODY, preventing case/default labels from resettingat_stmt_start— bare orelse after case labels in defer bodies leaked verbatim to C output (f439990)
Stats
- 20 bugs fixed, 15 security vulnerabilities patched
- +13 new test functions, +110 new compliance tests, total: 4,859 tests passing
- +1,131 lines of new test coverage
- 9 files changed, +1,412 insertions, −127 deletions
v1.0.7
Prism 1.0.7 continues the correctness and security focus, closing 26 bugs across the transpiler with particular attention to defer body emission integrity, two-pass invariant enforcement, volatile/VLA type tracking through struct bodies, and noreturn detection safety.
Security
- Lexical noreturn shadowing — a local variable or parameter shadowing a noreturn function name (
void (*exit)(void)) still got__builtin_unreachable()injected after the call — silent miscompilation turning reachable code into UB (018a0a0) - Volatile member memset — structs with
volatile-qualified fields were zero-initialized viamemset, which strips the volatile qualifier — compiler may optimize away stores to memory-mapped I/O registers (c81cff2) - C tag namespace collapse —
typedef_lookupcollapsed C's tag namespace (§6.2.3) with ordinary identifiers, soint MMIO = 1;created a shadow that hidstruct MMIO { volatile int x; }— volatile-member and VLA-member lookups on the struct tag silently failed (76c3ba1) typeof/_Atomicparen-skipping in struct body scanners —struct_body_contains_vla()andstruct_body_contains_volatile()skipped all(...)groups, makingtypeof(volatile int)andtypeof(int[n])fields invisible — volatile qualifier stripped or VLA missed (76c3ba1)- Backward goto over VLA — CFG verifier only checked defers between label and goto, not VLA declarations — a backward goto looping over a same-scope VLA re-allocates without freeing, enabling unbounded stack exhaustion (2bf9e0d)
constVLA memset —const typeof(int[len]) buf;emitted__builtin_memseton a const object — undefined behavior (C11 §6.7.3p6) (86be39a)registerVLA —register int buf[n];has no valid zero-init strategy (VLA can't use= {0}, register forbids address-taking for memset) — silent failure to initialize (86be39a)- Bare orelse VM-type double evaluation —
typeof(RHS)evaluates operand at runtime for variably-modified types; if the RHS function returns a pointer-to-VLA, the call fires twice (86be39a) - Defer body
ctrl_stateleak —EMIT_DEFER_BODYdidn't consumectrl_stateon{tokens, so afterif(cond)the pending state leaked into the braced body, wrapping declarations in spurious{ int x = 0; }— variable went out of scope immediately (ed9fd38)
Bug Fixes
defer:typeof-orelse expressions inside defer bodies were gated bymode != EMIT_DEFER_BODY— raworelsekeyword leaked verbatim to backend (ed9fd38)defer: bracket-orelse inside defer bodies had the sameEMIT_DEFER_BODYgate — compound literal and sizeof orelse leaked raw (ed9fd38)defer: paren-wrapped orelse(0 orelse return)inside defer bypassed Phase 1F validation —scan_decl_orelsestrips outer parens, making the return action reachable, corrupting defer stack unwinding (5c3657b)defer:ctrl_statenot reset after braceless body brace-wrap computation — leaked to next declaration, suppressingcheck_defer_var_shadow(c81cff2)defer: body emission loops (emit_block_body,emit_deferred_range) ranwalk_balancedon control-flow conditions withoutat_stmt_starttracking — declarations and labels insidefor()/if()inits bypassed zero-init and CFG checks (2bf9e0d)orelse:constexprcombined with orelse was not rejected by Phase 1D — runtime fallback emitted into a C23 compile-time constant declaration (86be39a)orelse: bracket orelsereject_orelse_side_effectspassedcheck_asm=falseat 4 call sites —asmstatements inside orelse LHS silently duplicated by ternary expansion (86be39a)orelse: Phase 1D multi-declarator split predicate was blind to orelse on the current declarator —int a = f() orelse 5, b;didn't trigger a split, emitting incompatible anonymous struct types (3cc88eb)orelse: Phase 1D split predicate only checkedhas_initfor the next declarator, missing VLA —typeof(int[n]) a = {0}, b;violated the two-pass invariant (3cc88eb)orelse: VM return-type double evaluation — bare orelse exempted bare function calls in RHS from side-effect rejection, but a function can return pointer-to-VLA, causingtypeof(RHS)to evaluate the call at runtime (86be39a)zeroinit:se_brace[8]capacity indefer_body_refs_namewas too small for 9+ nested statement expressions — conservative false-positive blocked valid defer variable references (86be39a)zeroinit: stmt-expr in ctrl condition label blindspot — Phase 1D balanced-group scan abort on({...})inif(...)left the closing)unprocessed, soat_stmt_startwas never set for the braceless body — labels and declarations invisible to CFG verification and zero-init (c2b7f68)- BOFrame fixed-size array overflow —
unsigned oe_ids[16], dim_ids[16]poisoned by inner stmt-expr declarations beyond 16 bracket-orelse entries — now arena-allocated with unbounded capacity (2bf9e0d) - HashMap arena leak —
HashMap.bucketsusedcalloc/free(heap) butFuncMeta.defer_name_setlives on the arena;arena_resetobliterated FuncMeta without freeing buckets — permanent heap leak per function-with-defer inPRISM_LIB_MODE(c81cff2) typeofcontrol-flow keyword rejection unconditionally errored onreturn/goto/break/continue/deferinsidetypeof()— broke glibcINLINE_SYSCALLmacro which expands to__typeof__(({...return...}))(b6f83b2)externdeclaration false positive as nested function — K&R fallback scan walked forward from;through subsequent code to find{(e.g. a switch body), falsely classifying the extern declaration as a nested function definition (ed38b7e)
Stats
- 26 bugs fixed, 9 security vulnerabilities patched
- +8 new test functions, +37 new compliance tests, total: 4,751 tests passing
- +1,352 lines of new test coverage
- 9 files changed, +1,744 insertions, −244 deletions
v1.0.6
Prism 1.0.6 is here, a focused correctness release that drops the _Generic member rewrite engine (moved to spec-only, no codegen) and fixes 18 bugs discovered through systematic full-pass auditing of the transpiler. Major areas addressed: C23 attribute handling across orelse/bracket/struct paths, Objective-C compatibility, typeof nesting, __label__ local labels, and the orelse keyword shadow disambiguation system — which now handles real typedefs named orelse uniformly via positional context.
Security
- Nested
typeof(typeof(VLA))misidentified as function type — memset skipped, leaving VLA uninitialized (63ab2b4) emit_block_bodymissing zero-init dispatch after labels/case in statement expressions — variables left uninitialized in({...})blocks (194f498)
Bug Fixes
orelse:typedef int orelse;followed byorelse x = get() orelse 42;produced untransformed output — 12-site fix unifying real typedef + shadow into positional disambiguation across Phase 1D and Pass 2 (63ab2b4)orelse: C23[[attr]]on first declarator stole bracket orelse FIFO queue entry meant for second declarator —int [[attr]] a[f() orelse 1], b[g() orelse 2];corruptedb's transformation (17b40d0)orelse: C23 attribute prefix leakedorelsekeyword into output — attribute noise tokens beforeorelsebypassed keyword detection (17b40d0)orelse: bare orelse comma-prefix range used rawemit_tokloop —rawkeyword leaked through inx = 1, y = f() orelse 0;(63ab2b4)raw:walk_balancedbracket subscript fast-path usedemit_tokinstead ofemit_tok_checked—rawkeyword leaked through[(raw int)x]cast expressions (63ab2b4)typeof:typeof(typeof(int[n]))inner(misidentified as function parameter list — suppressed VLA memset (63ab2b4)typeof: void function with trailing GNU__attribute__((noreturn))—walk_back_skip_attrsstopped at the attribute instead of reaching function name, causing false function-type identification failure (5299296)defer: struct/union/enum bodies inside defer false-positive zero-initialized members —struct { int x; }in defer body gotint x = 0;(194f498)defer: storage class / type specifier prefix on struct declaration inside defer bypassed SUE body detection —static struct S { int x; } s;in defer zero-initialized the struct body (194f498)emit_block_body: missing typeof orelse, bracket orelse, SUE body, and enum defer shadow dispatches in statement expression processing (194f498)zeroinit:_Pragma(...)and C23[[attr]]noise tokens at statement start inside({...})blocked zero-init detection —({ _Pragma("once") int x; x; })leftxuninitialized (5299296)zeroinit: Objective-C@interface/@implementationivar blocks treated as regular struct bodies —{ int _reserved; }after@interfacegot false zero-initialization (4d4e839)zeroinit: Objective-C protocol declarations and generics angle brackets<Type>mismatched as less-than operator — caused parse confusion in subsequent declarations (4d4e839)- C23
[[attr]]before named struct inwalk_back_skip_attrs— attribute tokens between struct keyword and name blocked backward walk, breaking zero-init and function-type detection (86a98d8) auto-unreachable:__builtin_unreachable()injected inside braceless control body in statement expressions —if (cond) noreturn_fn();inside({...})created multi-statement braceless body (86a98d8)- anonymous struct multi-declarator split detection used
skip_prep_dirsinstead ofskip_noise— C23 attributes before anonymous struct body bypassed the split rejection check (86a98d8) __label__: duplicate label names in Phase 1D name mangling produced identical mangled labels —__label__ a; __label__ a;in nested scopes crashed or produced wrong code (17b40d0)- macOS preprocessing missing
_DARWIN_C_SOURCEdefine — system headers using Darwin extensions failed during flatten (4d4e839)
Other Changes
- Dropped
_Genericmember rewrite engine — removed ~500 lines of codegen and 22 test functions;_Genericnow passes through untransformed (standard C) (1841bdc) - Unified
emit_block_body+emit_deferred_rangeinto 2-modeemit_statementsengine — single code path for all statement-level processing in defer bodies and statement expressions (17d642a) - Shrunk
P1FuncEntryandTypedefEntryfrom 40 → 32 bytes (2 per cache line); reducedskip_one_stmt_implstack from 82KB → 9KB (fe50fa9) - Updated SPEC.md: orelse shadow disambiguation section rewritten,
walk_balancedraw stripping,is_typeof_func_typenested typeof skip, 8 distributed positional check sites documented
Stats
- 18 bugs fixed, 2 security vulnerabilities patched
- +19 new test functions, total: 4,653 tests passing
- +214 net lines of new test coverage
- 12 files changed, +2,833 insertions, −2,817 deletions
v1.0.5
Prism 1.0.5 is here, this release is big, loads of internal refactoring to lay the groundwork for supporting new features, it is also focused entirely on correctness, security, and robustness — 67 bugs were fixed across every layer of the transpiler, with particular attention to the _Generic member rewrite engine, two-pass invariant violations, defer cleanup integrity, and orelse type safety.
This release also introduces a formal language specification with 307 compliance tests, it's a move towards robustness in documentation and validation, and moving prism toward much better support of ISO C99 / C11 / C23
Security
- Function type erasure —
typeof(func_typedef)through chained typedefs and directtypeof(int(int,int))signatures lost the function-type flag, emittingmemseton function type —.textsegment write / SIGSEGV (c92930a) - Initializer braces pushed
SCOPE_BLOCKinstead ofSCOPE_INIT, inflatingblock_depth— defer cleanup silently dropped on scope exit (6e926ac) - Forward-declared non-noreturn function tagged noreturn by attribute scanner, injecting
__builtin_unreachable()— unconditional crash (edae040) - K&R function name resolution walked past
token_pool[0]— out-of-bounds memory access (6dafccd) emit_defers_extriggered infinite recursion on cyclic scope reference — stack overflow DoS (6dafccd)_Genericmember rewrite used buffer-relative position invalidated by 128KBout_flush()— non-deterministic code corruption at buffer boundary (a694cb6)- Bare orelse
typeof(LHS)with indirection operators (*,->,.,[]) evaluated operand at runtime for variably-modified types — spurious volatile reads / double side effects (8fcca8a)
Bug Fixes
_Generic: ternary colon confused with association separator, blocking member rewrite on ternary branches (40b6fbc)_Generic: same-name-different-args — function with identical name but different arguments silently lost the argument list (6e926ac)_Generic: extraction discarded trailing expressions after close paren (a694cb6)_Generic: chain walk destroyed factory pattern suffixes —obj.factory().method()truncated toobj.factory()(a694cb6)_Generic: prefix multiplication on chained calls —get_api()->fetch()injected prefix before both identifiers (a694cb6)_Generic: ring buffer overflow on long prefix chains fell back to wrong code silently (e32bdc2)_Generic: paren-wrapped targetint: (handler)bypassed rewrite (e32bdc2)_Generic: multi-layer paren peeling not handled —int: ((handler))bypassed rewrite (452a220)_Generic:prefix_bufoverflow produced silent fallback instead of hard error (452a220)_Generic: ternary branch prefix drop — one arm of conditional target missed injection (3410f7d)_Generic: line directive desync on member rewrite rewind —#linetracking corrupted after output buffer rewind (3410f7d)_Generic: nested rewrite left orphan prefix text in output buffer (3410f7d)_Generic: inner-generic prefix drop — nested_Genericinside outer_Genericassociation lost the member prefix (60fc35e)_Generic: paren-complex target with cast prefix not handled (60fc35e)_Generic: array/member target boundary detection produced wrong rewrite range (60fc35e)_Generic: cast-prefix(type)handlerdropped during association value rewrite (c13effd)_Generic: nested rewrite blind spots in subtree emission loops —walk_balancedandwalk_balanced_orelsemissing hook (e966190)_Generic: member rewrite missing fromemit_deferred_range,emit_range_ex, andemit_token_range_orelse— defer bodies, const orelse init, and bracket orelse emitted_Genericverbatim;emit_token_range_orelseused rawOUT_TOKbypassinglast_emittedand emit save ring entirely (f69d460)defer: ghost enum defer shadow bypass in sizeof/cast/typeof expressions (edae040)defer: ghost enum defer shadow escape into enclosing scopes (452a220)defer: stmt-expr escape in control-flow condition heads inside defer bodies bypassed validation (edae040)defer: backward goto over same-scope defer loop — CFG verifier missed same-depth defer entries (3410f7d)defer: backward goto in nested scope used wrong ancestor-check direction — defer cleanup silently skipped (ae8a43f)defer: C23[[attr]]between function signature and body consumed into return-type typedef synthesis (2ac2200)defer:emit_deferred_range/emit_block_bodyflatemit_tokloop bypassed stmt-expr/orelse/zeroinit processing in control-flow conditions (6e926ac)defer: attributes encapsulating control-flow statements bypassed CFG verification (e966190)orelse:->operator matched as--in side-effect rejection check — false rejection of member access (8fcca8a)orelse: UB shift inorelse_shadow_is_kw— undefined behavior in transpiler itself (8fcca8a)orelse: bracket orelse stmt-expr reentrancy clobbered processing state — nested({...})in array dimensions corrupted context (e51e08b)orelse: orelse inside prototype VLA dimensions not rejected — produced invalid C (e51e08b)orelse:typeof_varmemset queue reentrancy clobber in stmt-expr array dimensions — pending memset entries silently overwritten (44ce49a)orelse:reject_orelse_side_effectsmissing control-flow keyword check —orelse goto Lnot detected as action form (44ce49a)orelse: RHS control-flow duplication — goto/return in chained orelse fallback emitted twice (c13effd)orelse: control-flow keywords inside type specifier positions not rejected (c13effd)orelse: C23constexprdeclarations with orelse not rejected — constexpr requires compile-time constant (c13effd)orelse: last-link bare orelse used ternary with usual arithmetic conversions — silently widened types across arms (e41533c)orelse: deferred orelse action not routed through full transpilation engine — blocks and keywords processed raw (e41533c)orelse: const-pointee VLA false rejection —const int (*p)[n]has const pointee, not const VLA (ae8a43f)orelse: stmt-expr paren-strip leaked keyword —(orelse)inside statement expression bypassed detection (6ca00ce)orelse:typeof_varreentry after fix produced wrong test result (c13effd)orelse: attr-raw VLA safety bypass — attributes betweenrawand VLA dimensions bypassed CFG check (e966190)zeroinit:__extension__prefix bypassed zero-initialization —__extension__ int x;emitted without= 0(edae040)zeroinit: for-init typedef type system desync —TT_TYPEDEFrouting missing inp1_scan_init_shadows(ae8a43f)raw: keyword leak in cast/sizeof/compound-literal —walk_balancedandemit_range_exmissingtry_strip_raw(edae040)raw: per-declaratorrawhidden behind attributes —int x, __attribute__((unused)) raw y;leakedrawinto output (e32bdc2)raw: bitfieldrawleak at colon —raw int x : 4;leaked keyword past colon (60fc35e)emit_type_rangeSUE body strip fooled by__attribute__arguments —packedmatched as struct name, stripping anonymous struct body (2ac2200)- O(N²) in
p1d_scan_balanced_groupon nested parentheses containing statement expressions (40b6fbc) sizeof((arr)[n])VLA false positive —)predecessor falsely signaled type context in expression position (6e926ac)skip_one_stmtelse-transition trail flush poisoning — parent cache entries corrupted by true-branch token caching (6e926ac)skip_one_stmttn=0on do-while entry destroyed parent trail — O(N²) rescans and incorrect cache values (6e926ac)]VLA false positive on multidimensional subscripts —arr[1][n]inner]falsely signaled type context (a694cb6)- MSVC diagnostic push/pop emitted nothing —
#pragma warning(push)/poppair missing (e32bdc2) _Float128suffix silent precision loss —1.0f128suffix not preserved during emission (e32bdc2)- Braceless typedef scope poison — typedef registered in braceless control-flow body persisted past body end (452a220)
- VLA multi-declarator sequence point split — VLA dimension expressions evaluated in wrong order after declaration splitting (452a220)
typeof_unqualoff-by-one const-stripping — qualifier removal consumed one token too few (3410f7d)- C23
enum Color : int { ... }fixed underlying type not parsed — colon consumed as label (60fc35e) - C23
constexprdeclarations silently skipped byTT_SKIP_DECLflag — transpiler never processed constexpr variables (c13effd) out_strlarge token flush tracking —out_total_flushednot updated for tokens exceeding 128KB buffer, corrupting absolute byte positions (e41533c)p1d_scan_balanced_groupflatinner_depthcounter didn't distinguish stmt-expr(from regular(— false error on valid code (e41533c)- Typedef array const-stripping removed
is_constfor array typedefs; added missingis_volatile/TDF_VOLATILEtracking (ae8a43f)
Stats
- 69 issues fixed (67 bugs, 2 crashes), 7 security vulnerabilities patched
- +53 new test functions, +307 formal compliance tests, total: 4,679 tests passing
- +6,066 lines of new test coverage
- 13 files changed, +8,165 insertions, −1,141 deletions
v1.0.4
Prism 1.0.4 — no new features. Internal cleanup, refactoring, and bug fixes.
Bug Fixes
orelse: ternary expression only caught by Pass 2 two-pass invariant breach (cf274c1)orelse:scan_decl_orelseparen unlinking miscompile inside larger expressions (5a4243d)orelse: stmt-expr LHS flat-emitted in condition wrap, bypassing defer/zeroinit (5a4243d)orelse: bare cast accepted non-lvalue (5a4243d)orelse: inside ctrl-flow condition parens two-pass violation (7fa5c85)orelse:p1d_decl_has_bracket_orelseparen-skip desync (7fa5c85)orelse: bare orelse without LHS expression leaks to Pass 2 (c4c9311)raw:raw * exprat statement start misclassified as pointer declaration, stripping token (c4c9311)zeroinit: parenthesized function typedef false positive (5a4243d)zeroinit:typeof(param)bypass when param shadows function name (c4c9311)zeroinit: param shadow registration missingp1_func_proto_mapcheck (c4c9311)zeroinit: noreturn attribute argument poisoning —__attribute__((cleanup(noreturn)))injected__builtin_unreachable(8abb51e)zeroinit: VLA deref paren adjacency —sizeof(*(param))hid*from backward look (8abb51e)defer: braceless defer shadow false positive —p1d_ctrl_pendingleaked into shadow checker (8abb51e)defer: O(N²) defer/decl shadow check via bloom filter saturation (c4c9311)
Refactoring
- Moved ~1,375 lines of parsing infrastructure from prism.c to parse.c
- Replaced
defer_name_bloom(64-bit bloom filter) withdefer_name_set(exact HashMap) - Extracted
defer_body_refs_name,is_stmt_expr_open(13 call sites) shared helpers - Deleted dead code:
defer_name_bloom_bit,INITIAL_ARRAY_CAP,has_zeroinit_decl,tok_name_eq, orelse guard code - Fixed
-Wsign-compareinequal_n
Stats
- 15 bugs fixed
- +11 new test functions, total: 4,147 tests passing
- prism.c: 10,829 → 8,867 lines (−1,962)
- parse.c: 2,094 → 3,609 lines (+1,515)
- Net: −447 lines of source code
- 9 files changed, +2,525 insertions, −2,627 deletions
v1.0.3
Prism 1.0.3 is here, no new features were added, this release is focused entirely on correctness, security, and robustness — 33 bugs were fixed across the transpiler's two-pass architecture, with particular attention to two-pass invariant violations, security-critical data corruption paths, and denial-of-service vectors.
Security
typeof((func))parenthesized function name bypassed function-type detection, emittingmemseton.textsegment — memory corruption (a5d566e)sizeof(n * int[n])commutative VLA rewriting bypassed the VLA detector — silent stack corruption (0abd764)- Deeply nested braceless
dostatements caused unbounded recursion inskip_one_stmt— stack overflow DoS (a945663)
Bug Fixes
defer: stmt-expr false positive in defer shadow checker (72fcf86)defer: braceless switchfind_switch_scope()leaked to outer switch, wiping its defer count (75ecedf)defer: C23 attribute bypassed defer stmt-expr chain check two-pass violation (a945663)defer: case/defaultp1d_ctrl_pendingleak bypassed CFG goto-over-declaration check (270b1fa)defer: shadow same-block two-pass violation between Phase 1D and Pass 2 (0abd764)orelse:typeofbit-field in bare orelse produced invalid temporary type (8888ae3)orelse: chained assignmenta = b = f() orelse 5split-brain found wrong=(a850967)orelse: VLA param decay false positive —sizeof(arr)on multidim VLA parameter (c2372aa)orelse:__auto_typecast in orelse temporary declaration (c2372aa)orelse: const-VLA orelse duplicated the VLA size expression (acf026b)orelse: GNU stmt-expr({...})in orelse fallback position not recognized (acf026b)orelse: ctrl-paren bracket orelse two-pass violation (acf026b)orelse:typeoforelse side-effect two-pass violation (acf026b)orelse: parenthesized(expr orelse val)two-pass violation (acf026b)orelse: bare orelse preprocessor conditional check two-pass violation (0abd764)orelse: volatile double-write via compound literal orelse fallback (270b1fa)zeroinit: block-scope function prototypetypeofemitted spurious memset on function type (a850967)zeroinit: for-init VLA memset two-pass violation (0abd764)zeroinit: param multidimensional VLAsizeoffalse positive (25cbe72)zeroinit: complex return type attribute consumed into typedef synthesis (25cbe72)raw: raw token leak past GNU/C23 attributes (a945663)- Nested stmt-expr
ctrl_save_stackhijack — inner({...})popped outer's save entry (a5d566e) skip_one_stmtcache poisoning false positive on if-else trail (270b1fa)skip_one_stmtlabel swallowing —L: if(1) {}overran statement boundary (75ecedf)array_size_is_vlaexponential blowup on nested bracket patterns (8888ae3)emit_type_rangestmt-expr bypass in struct bodies (0ea1478)- File-scope
typeofscope_treeNULL dereference (acf026b) - VM-type multi-declarator split two-pass violation (acf026b)
- Dead
const_td_is_arraycode removal (75ecedf)
Stats
- 33 bugs fixed, 3 security vulnerabilities patched
- +50 new test functions, total: 4,053 tests passing
- +2,293 lines of new test coverage
- 13 files changed, +2,934 insertions, −971 deletions
v1.0.2
Prism 1.0.2 is here, no new features where added, this release is heavily focused on quality, performance, and robustness to ensure it's dependable for all.
Performance
Prism's transpiler throughput improved by 13.4% (measured by retired instructions on self-transpile). Six hot paths were optimized with bloom filters, O(1) post-annotation typedef rejection, consolidated token metadata access, and a pure-C early exit for files with no orelse usage. (4650fc8)
Algorithmic Improvements
- Noreturn function tagging reduced from O(N·K) to O(N+K) via single-pass token annotation (46bb2a5)
- Typeof func-proto scanning reduced from O(N²) to O(N) per declarator (12ff66f)
- Nested bracket orelse scanning reduced from O(N²) to O(N) via iterative stack (6bb82d1)
- Noreturn taint propagation reduced from O(D×T) to O(T+D×E) edge-based fixed-point (2b422a8)
Bug Fixes
defer: orelse fallback block bypassed defer cleanup at scope exit (46bb2a5, fc098bb)defer: stmt-exprgoto/returninside orelse bypassed defer cleanup (6bb82d1)defer: scope/block depth confusion over-unwound defers across goto boundaries (4e79cd5)defer: stmt-expr goto bypassed defer cleanup in bare orelse comma expressions (4e79cd5)defer: braceless control-flow in defer/orelse bodies broke brace injection (fc098bb)orelse: raw string literalR"(...)"inside#if 0desynchronized block-comment state (46bb2a5)orelse:__attribute__bracket orelse queue desynchronization (5942783)orelse: keyword leak viastatic rawverbatim bypass (19032bc)orelse:init-castVLA orelse crash on variably-modified types (12ff66f)orelse: bare comma split escaped braceless control flow (4e79cd5)orelse: preprocessor conditional mangling in bracket orelse rejected in Phase 1D (2b422a8)orelse: paren comma corruption in chained expressions (fc098bb)orelse/defer: shadowed keywords suppressed by variable, enum, or typedef names (3f6e7c3)orelse/defer: braceless control-flow body wrapping conflicts with orelse handler (fc098bb)- MSVC test compatibility fixes (fc098bb)
- Recursion regression in deeply nested braceless
ifchains (2ec6a01)
Stats
- 23 bugs fixed, 6 performance optimizations
- +21 new test functions, total: 4,020 tests passing
- +1,246 lines of new test coverage
- 9 files changed, +2,502 insertions, −1,040 deletions
v1.0.1
Prism is out of its 1.0 stabilization phase, Prism is shifting to a more frequent release cadence.
This release also marks a fundamental shift in the nature of the project. Prism is no longer just a transpiler that adds explicit features like defer and orelse—it is evolving into a progressive enhancement engine for standard C. Developers get safer, faster, and tighter binaries simply by passing their code through the transpiler, without changing a single line of their source.
Progressive Enhancement: Auto-Unreachable Insertion
Prism now automatically tracks _Noreturn, [[noreturn]], and standard library exit functions across your entire translation unit. It now silently injects __builtin_unreachable() (or __assume(0) on MSVC) after these calls. (b3a6981)
Performance & Binary Size
By feeding explicit control-flow termination data directly to the backend C compiler, this enables aggressive dead-code elimination (DCE), allows the backend to drop unnecessary function epilogues, reduces register pressure, and improves branch prediction. Your binaries get smaller and faster automatically.
Bug Fixes
defer: cross-branch goto dropped cleanups — LCA exit gated behind flawed depth comparison (c3b824a)defer: braceless for-bodyif/elseprematurely cleared shadow scope (8017af3)defer: noreturn member namespace pollution injected false__builtin_unreachable()(6a0140e)zeroinit: typeof func-type scanner ignored paren depth in_Static_assert(sizeof(...))(c180878)zeroinit: CFG verifier missed braceless-scope declarations in switch (3da543b)orelse: static/extern persistence corruption — runtime re-init destroyed static semantics (05c61e2)orelse:typeof(RHS)double-evaluated VM-type expressions in bare assignment (10cf638)orelse: preprocessor directive in fallback concatenatedifbranches (887766d)
Stats
- 8 bugs fixed, 1 feature added
- +26 new test functions, total: 3,902 tests passing
- +758 lines of new test coverage
- 10 files changed, +935 insertions, −240 deletions
v1.0.0
This release represents a complete architectural rewrite of the transpiler from the ground up, graduating Prism from a single-pass prototype into a hardened, production-ready tool. Introduced the new orelse keyword, added full native Windows (MSVC) support, and massively expanded the test suite.
Most importantly, Prism is now fully self-hosting and fundamentally safer by design.
Major Highlights
- New Language Feature: The
orelsekeyword for elegant fallback values and control flow. - Native Windows Support: Full compatibility with MSVC (
cl.exe). - Two-Pass Architecture: A complete rewrite of the internal transpilation pipeline for much improved robustness.
- Robustness Hardening: Eliminated entire categories of parsing edge-case bugs.
- Massive Test Expansion: Over 2,700 new tests added.
- Formal Specifications: Technical
SPEC.md&DRAFT_SPEC.mddocumentation.
The Paradigm Shift: Two-Pass Architecture
Before, Prism used a single-pass architecture — it walked the token stream once, building the typedef table on the fly and emitting C as it went. Typedefs were popped from a mutable table on scope exit. Labels were scanned per-function right before emission. Goto safety was checked inline during code generation. It worked, but every edge case was a new special case bolted onto the emitter, and the emitter was already doing too much.
Now there are two distinct passes:
- Pass 1 walks every token at every depth before a single byte is emitted. It builds an immutable symbol table of every typedef, enum constant, parameter shadow, and VLA tag. It constructs a full scope tree — every
{/}pair with parent links and classification (loop, switch, function body, statement expression). It collects every label, goto, defer, case, and declaration into per-function entry arrays. Then a CFG verifier checks every goto→label and switch→case pair against defers and declarations. If your code is unsafe, Prism errors before writing a single byte. - Pass 2 is purely mechanical — it reads the immutable tables from Pass 1 and emits C. No typedef mutations, no scope tracking, no error decisions. Just output.
This separation closed entire categories of bugs. The old architecture had ~20 different places where the emitter had to make semantic decisions mid-output. Now it just reads annotations.
Architecture Comparison
- Execution: Single-pass emitter → two-pass (7 analysis phases + CFG verification + emission)
- Symbol Table: Mutable typedef table with scope-depth popping → immutable symbol table with token-position-based range lookups
- Label Scanning: Per-function label scan at emit time → all labels/gotos/defers collected in Pass 1, verified in Phase 2A
- Goto Safety: Inline goto safety checks during emission → O(N) snapshot-and-sweep CFG verifier with hash-based label lookup
- Scope Tracking: Ad-hoc scope tracking → full scope tree with parent links, classification, and ancestor queries
Scale & Stability By the Numbers
- The Codebase: 13,132 insertions and 7,287 deletions across 287 commits. The core transpiler grew from 5,839 to 13,494 lines of C (
prism.c+parse.c+windows.c) to accommodate the deep semantic analysis phases. - The Test Suite: Expanded from ~1,000 to 3,795 tests across 9 suites (adding over 37,916 new lines of test code).
- CI/CD Pipeline: Now continuously tested across 6 diverse platforms: Linux x86_64, macOS x86_64, macOS ARM64, Windows build-only, Linux ARM64, Linux RISC-V64.
- Self-Hosting: Prism transpiles itself, the output compiles, that binary transpiles Prism again, and the two outputs match byte-for-byte.
Documentation & Specs
- SPEC.md — 785-line transpiler specification. Every item corresponds 1:1 to implemented behavior exercised by the test suite. This is not aspirational — it describes what the transpiler does.
- DRAFT_SPEC.md — Ideas under consideration. Nothing here is implemented. Items may be adopted, modified, or rejected. Once implemented, they move to
SPEC.md.
v0.110.0
Prism v0.110.0 is a major release that adds native Windows support(MSVC) significant performance gains, hardening across the transpiler, parser.
1.60× faster end-to-end.
| Version | Mean | Range |
|---|---|---|
| v0.105.0 | 317.4 ms ± 7.0 ms | 303.7 ms … 330.7 ms |
| v1.1 | 198.0 ms ± 3.1 ms | 192.0 ms … 207.6 ms |
Full compile pipeline, prism prism.c -o /dev/null, 30 runs (hyperfine)
- Binary size (Linux): 79K → 99K (+25%)
- Tests: 1,032 → 1,276 (+244)
Performance
- Fixed ~10× performance regression in token walker (750179f)
- Pipe-based preprocessing — transpiler pipes directly to system compiler, skips preprocessor on second pass (f194f56)
- Walker brace optimization + inline hot functions (
emit_tok,new_token, walker) (76872b4) - Tag dispatch for faster token classification (76872b4)
- Big optimization pass with fast paths throughout (c4104bb, cc44676)
Fixed
- Label zero-init bypass —
:now treated as statement boundary (eac1eb1) - POSIX macro force-override — checks user
#defines before appending defaults (eac1eb1) typeof(volatile)memset — scans insidetypeof()for volatile qualifier (eac1eb1)- Block comment and raw string newline tracking (29c951b)
scan_line_directiveinteger overflow (29c951b)emit_expr_to_semicolonternary:safety via depth tracking (29c951b)scan_pp_numberrejecting'before non-hex letters (e2a27af)- Silently skipped zero-init in edge cases (48a4830)
- Void typedef return bug (b9b6883)
- Hashmap zombie resize corruption (2e29aca)
make_temp_filetemplate truncation (2e29aca)- Typedef de-duplication (949f805)
typeofvars hard limit removed — dynamic allocation (6b72b4f)assert.hemitted twice (f58464f)path_basenameseparator ordering (3c2e355)- Pipe deadlock (949f805)
__builtin_memsetused withoutstring.hdependency (949f805)- Parser depth limit to prevent stack overflow (5ec9f63)
- And many more
Hardened
ENSURE_ARRAY_CAP:int→size_toverflow prevention, capacity reset on OOM (0823077)new_file: free contents before error return,strdupNULL check (0823077)argv_builder_add:strdupNULL check (f58464f)tokenizer_teardown: NULL guard oninput_files(228561a)system_includes_reset: NULL guard onsystem_include_list(228561a)error(): NULL guard onctxinPRISM_LIB_MODE(29c951b)warn_toksuppressed in lib mode (eac1eb1)- Lib mode OOM leak —
init_keyword_mapmoved beforepp_bufallocation (0823077) - TOCTOU removed in
make_temp_file(0823077) realloc/setjmphandling hardened (6b72b4f)- Hashmap tombstone handling (5ec9f63)
- File view cache memory fix (1e66c01)
- Per-transpilation memory leak in lib mode (1e66c01)
Testing
- 1,276 tests (up from 1,032)
- Regression tests for K&R defer, label zero-init,
typeof(volatile), raw*ptrdeclarations, VLA zero-init,scan_pp_number, typedef edge cases (e2a27af, 48a4830, 0823077, eac1eb1, 5ec9f63)
Misc
- K&R-style functions now get
deferand label support (eac1eb1)
v0.105.0
Prism v0.105.0 is a heavy consolidation release focused on hardening Prism and dramatically improving code quality. This update refactors significant portions of the parsing logic to be faster and more maintainable while closing complex safety holes in control flow analysis.
- Code (prism.c + parse.c): 7,649 → 5,518 LOC (-27.9%)
- Binary size (Linux): 107K → 79K (-26.2%)
Fixed
gotoskipping for-init declarations during mixed CLI flags- Scope-leak bugs hardened
deferedge cases bulletproofed
Changed
- Unified token walker with speedup in classification (1dabdde, 99568a6)
- Tag-based token system with unified declaration parser (a06201b, ee08aea)
- Table-driven CLI parsing, token spacing, and loop handling (ee08aea, d09b376, 23deede)
- Merged control-flow state tracking (1d17dd4, 85f12cf)
- Algebraic token spacing logic (23deede)
- Simplified VLA logic (a1808d0, 6cc5aa0)
- Simplified UTF-8 identifier handling (2d99fff)
- Simplified output buffering with
setvbuf(06b7886) - Simplified feature flags and control state reset (85f12cf)
- Merged type parsing (b5b1525)
- Merged VLA and defer handling (6cc5aa0)
- Unified temporary file creation (d09b376)
- Consolidated switch-scope handling (23deede)
- Merged goto skip logic (d09b376)
- Less verbose error messages (18a9980)
Removed
- Parsing not needed for Prism core (967aa9d)
- Dead typedef fallback code (23deede)
- Old CLI logic (5e9453b)
- Duplicate array handling code (18a9980)
- Duplicate error handling (1d17dd4)
- Refactored globals towards thread safe lib mode (e67265b)
Testing
- Added 823 lines of test coverage
- Total: 1,032 tests
v0.100.0
First single pass version of prism, this is unsafe and a very early prototype, do not use.