Skip to content
← Prism
Prism
Overview
Features
defer orelse zeroinit raw bounds-check auto-unreachable auto-static check
C in practice
Overview goto skips init tag namespace struct padding strict aliasing char signedness signed overflow VLAs segfaults
Spec Draft Releases Blog
GitHub ↗

ReleasesPrism changelog and release history

Prism release history. All releases are available on GitHub Releases.

#v1.1.7 current

GitHub ↗

Prism 1.1.7 hardens transpilation, compiler invocation, output publication, cache correctness, and language-feature parsing.

The procedural regression suite grows by 838 cells to 46,334 platform-inclusive cells, faster test suit.

#fixes

  1. --prism-emit now publishes output atomically.
  2. --prism-emit rejects an input file or hard link as its destination, preserves an old destination on failure, and rejects one explicit destination for multiple sources.
  3. Stdout emission uses the caller's descriptor on POSIX and reports write, flush, and close failures on every output path.
  4. Generated #line filenames now escape quotes, controls, and trigraph-forming question marks.
  5. Raw stripping keeps directives on their own line.
  6. Stray braces now diagnose safely instead of reaching emission in an invalid parser state.
  7. Malformed file-scope orelse now diagnoses safely.
  8. Illegal bracket orelse dimensions now diagnose safely.
  9. Declaration conditions now diagnose safely.
  10. Statement-expression gotos now diagnose safely.
  11. Parser arena shrinking is safe for library callers.
  12. First-context allocation failure is safe for library callers.
  13. Result-error allocation failure is safe for library callers.
  14. Source-directive recovery is safe for library callers.
  15. prism_reset() is safe for library callers after parser errors.
  16. prism_thread_cleanup() is safe for library callers.
  17. prism_free(NULL) is safe for library callers.
  18. File APIs reject directories and non-C inputs before invoking the compiler.
  19. File APIs reject malformed feature-array counts safely.
  20. Oversized preprocessor argv construction is rejected before unsafe allocation.
  21. File APIs reject embedded-NUL input before unsafe processing.
  22. File APIs reject UTF-16-looking .i input before unsafe processing.
  23. Compiler processes receive a fresh environment snapshot for every spawn.
  24. Windows preserves Unicode environment values and rejects malformed UTF-8 conversion instead of silently changing it.
  25. Stdin preprocessing normalizes -x none to C.
  26. Re-emitted API definitions preserve preprocessor order and last-definition-wins behavior.
  27. Quoted custom compiler commands preserve embedded spaces and platform quote rules without shell parsing.
  28. Response files now expand lazily, respect check and -- boundaries, work as option operands, and support nesting and pipes.
  29. Response files support UTF-8 and Windows UTF-16 BOMs and handle malformed reads safely.
  30. MSVC /c, /Fe, /Fo, /FI, /D, /E, /EP, /P, stdin, and slash-option operands are modeled correctly.
  31. GNU -f... flags no longer collide with MSVC dash spellings.
  32. Non-flattened direct system headers retain the original preprocessor stream when re-emission could change macro, pragma, include, or reinclusion ordering.
  33. The preprocessor cache validates payload checksums, full file identity, working directory, Unicode environment inputs, and volatile header macros.
  34. The cache bypasses dependency side effects, marker suppression, response files, specs files, volatile flags, and custom compiler wrappers.
  35. Cache publishing uses unique temporary files, saturated limits, and safe unavailable-admin paths.
  36. Braceless defer now captures an entire control statement, including if/else, loop, switch, and do/while bodies.
  37. Braced defer bodies validate embedded break and continue, preventing an invalid deferred escape from reaching emitted C.
  38. Bounds checking now rejects a conditional base derived from tracked arrays instead of using one branch's extent for another.
  39. Auto-static no longer mistakes shadowable true or false identifiers for constant initializers.
  40. Deferred-capture discovery excludes actual orelse operators.
  41. Deferred-capture discovery excludes raw declaration prefixes and raw blocks.
  42. defer, orelse, and raw now recognize literal backslash-LF and backslash-CRLF splices before soft-keyword matching.
  43. C trigraph translation now runs before splice handling, so a trigraph-derived backslash such as ??/ followed by LF/CRLF also joins a Prism keyword correctly.
  44. Generated #line directives now retain the physical source line after a splice instead of mapping the next token to the preceding line.
  45. A function that returns a function pointer now synthesizes its defer return-type typedef with the returned type's parameter list. int (*f(void))(int) produced typedef int (*T);, so the return temporary was an int *; GCC 16 and Clang 22 reject the resulting initializer and return outright, and older compilers accepted them with incompatible-pointer warnings. Array-pointer returns were already correct; (params) was the one declarator suffix being dropped.
  46. A return value expression with no terminating ; now diagnoses instead of copying the rest of the translation unit into the return temporary's initializer and emitting unbalanced C.
  47. Defer bodies, and the synthetic blocks spliced for return, break, continue, and goto, no longer land on a surviving preprocessor directive's line. A conforming preprocessor discards trailing tokens on a directive line, so with -fno-line-directives a #pragma immediately before a scope exit silently deleted the cleanup, and in the return case the return statement with it. Line directives happened to force the line break, which is why the failure only appeared with them disabled.
  48. Macro-hygiene wrap parentheses around a declaration initializer no longer hide an empty orelse action. int x = (f() orelse); was accepted and lowered to x = x ? x :;, while the unwrapped int x = f() orelse; was already rejected.
  49. An unterminated GNU __label__ declaration no longer duplicates every token from the declaration to end of input. The emitter wrote the tokens before discovering there was no terminator, then reported the statement as unhandled, and the caller emitted them a second time.
  50. --prism-emit=<file> publishes with the destination's previous mode when replacing one, and with 0666 & ~umask when creating one. Atomic publication is via mkstemp, which forces 0600.
  51. Trigraph translation no longer runs twice. cc -E (and a .i input) has already completed translation phases 1 to 3 with the backend's trigraph policy, which is off by default for GCC, Clang and MSVC and removed outright in C23. Re-applying phase 1 to that stream rewrote literals the compiler had deliberately kept: const char *s = "what??!"; compiled to what| under Prism and what??! under the very compiler Prism was driving. Prism now matches cc exactly under -std=c99, -std=gnu99, -std=c11, -std=c2x, -trigraphs and the default. Raw source reaching the library API is unaffected: Prism is the only front end that text sees, so it still owns phases 1 and 2, and the spliced-keyword forms keep working.
  52. An orelse fallback value with no terminating ; now diagnoses instead of copying the rest of the enclosing block into LHS = ( ... ). Same defect and same shape as fix 46, on the bare-assignment path.
  53. A defer directly after a GNU __label__ declaration is no longer rejected as expression context. Phase 1D handed on the __label__ token itself as the previous token, so the following statement looked like a continuation of an expression; valid GNU C plus valid Prism was refused, and only when nothing else stood between the two.
  54. A __label__ declaration as a braceless defer body now diagnoses. It carries no type tag, so the existing braceless-declaration check never saw it, and the body scan emitted a stray }.
  55. A bare orelse whose Phase 1 recipe has no recorded assignment = no longer dereferences a null token. Found by UBSan in the fuzz target.
  56. Extension elimination is enforced at emission instead of assumed. A token Phase 1 classified as the keyword, whose feature is enabled and which is not inside a raw { ... } block, must be consumed by a lowering path; reaching raw emission now diagnoses. Three retired-regression cells were locking in the opposite: defer { int x = 5 } with no ; had the rest of the file swallowed into the deferred body and pasted back, so the output declared task_b twice and contained a live defer; and an attribute between if and its ( left p = get() orelse 0; in the generated C. All three inputs are rejected by a C compiler on their own merits, and all three previously produced output no compiler could use. The check costs one predictable branch on an already-loaded tag word.
  57. Library calls no longer inherit the line-directive spelling from an earlier call. use_linemarkers selects GCC's # N "f" linemarker over C99's #line N "f"; only the CLI ever assigned it, and no library entry point reset it, so once anything in a thread set it every later prism_transpile_source silently emitted the other spelling. Found by sharding the suite: a shard that ran the internal-platform recipe next to the feature matrix lost #line from all 64 of its line-directive cells.
  58. Every posix_spawn site retries the transient failures. Four of the five had no retry at all -- including the preprocessor spawn -- and the one that did covered EAGAIN and ETXTBSY but not ENOMEM, which is what macOS returns when the machine is briefly short of memory.
  59. --prism-emit to a destination that is not a regular file (/dev/null, a character device, a fifo) now streams the finished output instead of failing. rename cannot publish onto such a destination, and the fallback also covers a cross-device EXDEV when the sibling temporary had to fall back to $TMPDIR. The destination is still opened only after translation and verification succeed.
  1. The Windows build links again. Fix 58's retry helper called nanosleep, which has no MSVC import. The call had existed for a long time inside spawn_command, whose every caller is POSIX-only, so the linker discarded the function whole and the symbol never had to resolve; giving the retry four more call sites that are live on Windows turned it into unresolved external symbol nanosleep. The sleep is now Sleep on Win32 and nanosleep elsewhere, and the helper's parameters match windows.c's posix_spawnp shim rather than the POSIX prototype, since a non-const argument converts to POSIX's const one but not the reverse.
  2. windows.c includes <basetsd.h> in lowercase. The MSVC SDK is case-insensitive so both spellings work there, but mingw-w64 ships the lowercase name and a case-sensitive host could not resolve <BaseTsd.h>. This is what had made the _WIN32 source path uncheckable anywhere except Windows CI, which is why fix 60 reached a push.
  1. The suite's two infrastructure counters are declared outside the POSIX guard. infra_retries and slow_clock_skips are read by the summary in main(), which is compiled on every platform, but sat beside the POSIX-only code that increments them; MSVC reported four undeclared identifier errors. Same shape as fix 60 -- code added under one platform's assumptions and never built under the other.
  2. Bounds checking covers int a[static N]. C11 6.7.6.3p7 promises the argument points at at least N elements, and that promise is the only extent an array parameter has -- it decays to a pointer, so the usual sizeof(a)/sizeof(a[0]) would measure the pointer against the element. The bound is the literal N. [static const N] and [const static N] are both recognised; an array parameter without the promise, and a plain pointer parameter, stay unchecked as before.
  3. Bounds checking covers pointer-to-array dereference. int (*p)[4] carries its extent in the type, and *p and p[0] denote the same object, so (*p)[i], p[0][i] and (*(p))[i] all bound against sizeof(p[0])/sizeof(p[0][0]). The pointer hop itself stays unchecked -- nothing says how many arrays p points at -- so p[i] is untouched, and a base built from a pointer-to-array in any other shape is left alone rather than diagnosed.
  4. Header-bearing preprocessor-cache entries are enabled. Recording each header's identity was never the missing piece; knowing whether it is still the file the next preprocess would choose was. An entry now also records the backend's include search path, taken from -E -v with the same flags, and each dependency's spelling as the preprocessor reported it. A file appearing ahead of a header in the search path moves that directory's mtime; a symlink retargeted anywhere along the way changes what the spelling resolves to. Either one is a miss. Failing to determine the search path means the entry is not published, so the fallback is exactly the previous behaviour. PP_CACHE_MAGIC advances to PRISMPPC4; older entries are ignored.
  1. Macro operands of surviving #pragma directives are preserved. No preprocessor expands a pragma's operands, so flattening -- which hands the preprocessed stream through and re-emits no source defines at all -- left #pragma pack(push, PK) in the output with PK defined nowhere. A define named by a surviving pragma is now kept. Where it goes depends on where it was: a define ahead of the first include is hoisted with the rest, and one that follows a header is emitted in place, immediately before the pragma that needs it, because a define written after a header may be completing or shadowing what that header set up and must not be lifted above it. Defines no pragma names are still dropped. Preserving the define makes the question of whether a given backend expands pragma operands moot: it now does whatever it would have done with the original source.
  2. -fno-orelse leaves the word orelse alone. Phase 1D annotated and then diagnosed the declaration-initializer form without consulting the feature bit, so int j = w || orelse; was rejected with the feature off, where the word is an ordinary identifier and an undeclared one is the backend's complaint to make in its own words. It also meant Prism could not reparse its own output whenever an input used the name as a plain identifier -- the reparse oracle runs with every transformation disabled.
  3. An expression walk that reaches } without its ; diagnoses instead of consuming the closing brace. This was the last of the four expression walks still missing the bound that fixes 46 and 52 added to the others; found by the fuzz corpus.
  4. typeof-based zero-init at file scope no longer indexes a negative function slot. Zero-init lowers to a runtime statement -- a byte loop or a memset -- which has no meaning outside a function, and the emitter reached for the enclosing function before checking there was one.
  5. A spawn the machine refused is reported as such instead of as a preprocessing error. The library returned PRISM_ERR_IO with "Preprocessing failed" whether the preprocessor had run and disagreed or the machine had declined to start it at all -- two conditions that need different responses, and only one of which is about C. The message now names the refusal and its errno. The retry budget behind it also grows from five attempts over 0.2s to nine over ~2.8s: a Mac with 14.9 GB paged out refused straight through the old window, and waiting longer costs nothing on a machine where the first attempt succeeds.

#v1.1.6

GitHub ↗

Prism 1.1.6 fixes 41 issues with new procedural test suite with 45,496 test cells, performance is 1.25x faster than 1.1.5 and 1.59x faster than 1.1.4. Warm cached builds are 2.87x faster than 1.1.4.

new test suit measured coverage is 93.35% lines, 75.23% branches, and 99.05% functions.

#fixes

  1. Separate-argument compiler flags are parsed correctly. This includes -isysroot, -imultilib, -aux-info, -dumpbase, -dumpbase-ext, -dumpdir, -wrapper, --param, and supported ld64 flags. SDK paths no longer become extra source files on macOS.
  2. A block-comment line ending in \ no longer causes the next live #define to disappear in non-flatten mode.
  3. Function-like macros now use a valid identifier-only guard. #define SUM(a,b) is guarded by #ifndef SUM, not #ifndef SUM(a,b).
  4. idx[&(a[0])] and equivalent nested forms can no longer bypass commutative-subscript rejection.
  5. Pointer-arithmetic dereferences after braceless if, while, for, and switch statements are rejected instead of passing through unchecked.
  6. Address expressions after braceless control conditions no longer receive a false runtime check on a legal one-past pointer.
  7. }, ++, and -- are recognized as value tokens when distinguishing binary & from address-of, so the following subscript remains checked.
  8. Address-of suppression now spans parentheses. &(a[n]) permits the same legal one-past pointer as &a[n].
  9. The Windows mkstemps shim rejects templates without replacement X characters instead of opening a predictable fixed path.
  10. The first library call targeting MSVC now emits matching MSVC diagnostic pragmas. It no longer starts with a GCC push and ends with an MSVC pop.
  11. --prism-verify now counts dialect keywords with the tokenizer. Raw strings, prefixes, UCNs, digraphs, comments, and macro shadowing can no longer hide a leaked defer or orelse token.
  12. Nested groups in a defer expression now share the 4096-level parser limit. Excessive nesting reports a diagnostic instead of overflowing the stack.
  13. setjmp taint now propagates through same-file call edges, preventing defer in callers that can be bypassed by longjmp.
  14. Casted calls such as (void)wrapper(p) no longer hide transitive setjmp or vfork taint.
  15. Calls to a declared function named defer keep their arguments. defer(1) is no longer mistaken for the language construct.
  16. Calls through a function-pointer value named defer are preserved for local, ANSI parameter, typedef parameter, and K&R parameter forms.
  17. Library-mode defer inside an unresolved preprocessor arm is rejected instead of being moved outside the conditional and made unconditional.
  18. A do-while block containing trailing defer in a GNU statement expression now receives the same required diagnostic as equivalent block forms.
  19. C23 constexpr values are treated as integer constant expressions for array sizing instead of as VLAs.
  20. A parameter shadowing a constexpr binding now restores VLA behavior inside the function.
  21. File-scope symbol discovery now requires a valid function declarator. defer (void)0; can no longer register defer as a function and evade the file-scope diagnostic.
  22. Unevaluated state now survives nested delimiters and comma expressions in sizeof, typeof, and _Generic control expressions. _Generic association expressions remain evaluated.
  23. Qualifiers inside balanced sizeof, alignof, and offsetof operands no longer leak into the result type of an enclosing typeof.
  24. Const typeof objects use a declaration initializer when legal instead of delayed byte writes through a const object.
  25. Direct typeof(variable) lookup preserves atomic qualification. Unsupported const atomic zero initialization now reports a diagnostic.
  26. Direct typeof(variable) lookup preserves const qualification, while typeof_unqual removes it.
  27. Typedefs derived from typeof retain that origin through alias chains, preserving whole-object initialization for types such as x86 long double.
  28. Top-level volatile and _Atomic qualifiers on pointer declarators and pointer typedefs are no longer confused with pointee qualifiers.
  29. ANSI parameters retain exact const, volatile, atomic, pointer, and adjusted-array traits for typeof lookup.
  30. K&R parameter declarations now register const, volatile, atomic, and long-double traits instead of only VLA traits.
  31. Direct, typedef, complex, and volatile x86 long double locals now clear the full object, including x87 padding bytes.
  32. Every link in a chained bare-assignment orelse now receives the variably-modified side-effect check. Middle links can no longer evaluate twice.
  33. A declaration fallback ending in a preprocessor directive now places its generated semicolon after the directive line instead of producing #endif;.
  34. A nonzero PrismFeatures array count with a NULL array pointer is normalized safely instead of dereferencing NULL.
  35. Sparse define, include, flag, and forced-include arrays are checked by every consumer, including non-flatten re-emission.
  36. Negative feature-array counts are normalized before capacity arithmetic, preventing integer overflow.
  37. prism_transpile_file(NULL, ...) returns a deterministic IO error without invoking the compiler.
  38. Removing raw at the start of a line now preserves the newline. Declarations after pragmas no longer merge into the pragma line.
  39. prism install creates missing parent directories such as $HOME/.local/bin instead of failing and retrying with sudo.
  40. Reinstalling through a symlink, relative path, hard link, or path containing /../ recognizes the existing binary by file identity and avoids copying it onto itself.
  41. All ten feature flags accept positive and negative forms. Prism consumes only exact feature names, so unrelated flags such as -fdefer-pop and -fno-strict-aliasing still reach the backend.

#v1.1.5

GitHub ↗

Prism 1.1.5 fixes 200+ bugs since v1.1.4, Prism gets faster ~2.02x end-to-end warm cache; 1.65x on the stress TU with the cache disabled.

New: preprocessed-output cache, prism check <analyzer>, @file response files, stdin input.

Four fixes address bugs that corrupt a build: a uint16 scope-cap that dropped the scope tree past 32,768 scopes with no diagnostic; orelse reaching the C backend intact inside a case label; a braceless defer declaration that copied every following statement into the cleanup; and orelse on a volatile or _Atomic object reading it up to three times.

#Performance

Preprocessed-output cache. cc -E was 39 to 99% of wall time (8.19 of 8.28 ms on a no-include file; 103.8 of 262.8 ms on test.c). Prism now caches the preprocessor's output and skips the spawn when nothing it read changed.

The preprocess phase drops 10 to 25x on a hit, scaling with how much output has to be read back: 6.5 → 0.4 ms for a 57 KB entry, 29.0 → 1.2 ms for 328 KB, 46.2 → 1.8 ms for 1.2 MB. End-to-end that is 2.02x v1.1.4 on a warm cache (70.9 → 35.0 ms, best-of-3 over four suite files).

Transpiler. Gains are workload-dependent, and the cache accounts for most of the end-to-end figure above. With PRISM_NO_PP_CACHE=1, on a 2,500-function defer/orelse/-fbounds-check stress TU it is 1.65x (70.9 → 43.0 ms); across four ordinary suite files 1.07x, ranging 1.20x on test.torture.c down to 0.94x on test.defer.c. Earlier notes reported ~65% on the phase timings of a stress TU; that holds for that shape, not as a general figure.

On Arch/9950X, GCC -O2 -g -DNDEBUG, CPU 8 pinned, 80-run perf stat batches: the 6,091,701-byte procedural TU moves 77.66 → 70.32 ms task-clock (−9.45%), cycles −10.26%, instructions −10.36%. The 1,109,463-byte self-host TU moves 15.94 → 15.05 ms (−5.58%). Outputs byte-identical.

#Bug Fixes

#defer

#orelse

#bounds-check

#zero-init

#auto-static / auto-unreachable

#raw

#tokenizer / preprocessor

#driver / CLI

#Windows / MSVC

#assertions / harness

#Testing

104 generative tiers Prism test suit moves to more procedural tests to cover much wider set of edge cases. suites test.autostatic.c, test.autounreach.c, test.golf.c, test.cert.c and test.raw.c were absorbed into generative tiers.

#Code organization

C-language logic moved out of prism.c into parse.c, which is moving towards a reusable C-parsing library with Prism dialect support. parse.c 4,595 → 12,928 lines; prism.c 12,336 → 9,696.

#Stats

#v1.1.4

GitHub ↗

Prism v1.1.4 is a bug-fix release: ~105 bugs across 8 fix commits, including 8 bounds-check bypass vectors, ~62 C23 init-statement false rejections, soft-keyword identifier corruption in the parser, const+orelse aggregate UB, computed-goto false positives, and a #pragma link parser crash. Also: optimization-attribute support and a Windows thread-attribute fix.

#Security

#Bug Fixes

#Features

#Stats

#v1.1.3

GitHub ↗

Prism 1.1.3 is a bug-fix release after 1.1.2. Focus: contextual keyword handling, C23/extension keyword identifier cases, orelse disambiguation, and the Windows CI regression around defer union zero-init checks.

#Bug Fixes

#Stats

#v1.1.2

GitHub ↗

Prism 1.1.2 adds -fbounds-check: runtime subscript trapping for tracked arrays, without wrapping declarator contexts, typedef chains, multi-dimensional arrays, or parenthesized variants incorrectly. It also adds two CLI helpers, plus bounds-, defer-, orelse-, and phase-1 scope-tracking fixes found while shipping the feature.

This release also fixes build-system issues, including Meson.

#Features

#Security

#Bug Fixes

Bounds-check false positives and spurious wraps

Defer, orelse, zero-init

Other

Windows / cross-platform

#Stats

#v1.1.1

GitHub ↗

Prism 1.1.1 is bug fixes only: 27 bugs across 9 commits, including memory-safety issues in the preprocessor pipeline, multiple zero-init bypass vectors, defer/orelse interaction breakage, and emitter-level codegen drift.

#Security

#Bug Fixes

#Stats

#v1.1.0

GitHub ↗

Prism 1.1.0 introduces auto-static promotion and bug fixes.

#New Features

#Security

#Bug Fixes

#Tests

#Stats

#v1.0.9

GitHub ↗

Prism 1.0.9 fixes 17 bugs across defer validation, orelse transformation, zero-initialization, and the typedef table, including chain-aware scanning in defer bodies and orelse side-effect validation.

#Security

#Bug Fixes

#Stats

#v1.0.8

GitHub ↗

Prism 1.0.8 closes 20 bugs across the transpiler, including keyword table coverage, namespace isolation, statement-expression dispatch completeness, and braceless control flow scope tracking.

#Security

#Bug Fixes

#Stats

#v1.0.7

GitHub ↗

Prism 1.0.7 closes 26 bugs across the transpiler, including defer body emission integrity, two-pass invariant enforcement, volatile/VLA type tracking through struct bodies, and noreturn detection safety.

#Security

#Bug Fixes

#Stats

#v1.0.6

GitHub ↗

Prism 1.0.6 drops the _Generic member rewrite engine (spec-only, no codegen) and fixes 18 bugs found in a full-pass audit. Areas covered: C23 attribute handling across orelse/bracket/struct paths, Objective-C compatibility, typeof nesting, __label__ local labels, and orelse keyword shadow disambiguation (including real typedefs named orelse, via positional context).

#Security

#Bug Fixes

#Other Changes

#Stats

#v1.0.5

GitHub ↗

Prism 1.0.5 includes internal refactoring for upcoming features, plus correctness and security fixes: 67 bugs across the transpiler, including 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, expanding validation toward ISO C99 / C11 / C23.

#Security

#Bug Fixes

#Stats

#v1.0.4

GitHub ↗

Prism 1.0.4: no new features. Internal cleanup, refactoring, and bug fixes.

#Bug Fixes

#Refactoring

#Stats

#v1.0.3

GitHub ↗

Prism 1.0.3 adds no new features. Focus is correctness and security: 33 bugs fixed across the two-pass architecture, including two-pass invariant violations, security-critical data corruption paths, and denial-of-service vectors.

#Security

#Bug Fixes

#Stats

#v1.0.2

GitHub ↗

Prism 1.0.2 adds no new features. Focus is quality, performance, and correctness.

#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

#Bug Fixes

#Stats

#v1.0.1

GitHub ↗

Prism leaves the 1.0 stabilization phase and moves to a more frequent release cadence.

Prism now also applies progressive enhancements to plain C, not only explicit features like defer and orelse. Passing unmodified source through Prism can produce safer defaults and smaller binaries without source edits.

#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

Feeding explicit control-flow termination data to the backend C compiler 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

#Stats

#v1.0.0

GitHub ↗

This release rewrites the transpiler from a single-pass prototype into a two-pass design. It adds the orelse keyword, full native Windows (MSVC) support, and a larger test suite.

Prism is now self-hosting.

#Major Highlights


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

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


#Scale


#Documentation & Specs

#v0.110.0

GitHub ↗

Prism v0.110.0 adds native Windows support (MSVC), performance gains, and hardening across the transpiler and parser.

#1.60× faster end-to-end.

VersionMeanRange
v0.105.0317.4 ms ± 7.0 ms303.7 ms … 330.7 ms
v1.1198.0 ms ± 3.1 ms192.0 ms … 207.6 ms

Full compile pipeline, prism prism.c -o /dev/null, 30 runs (hyperfine)

#Performance

#Fixed

#Hardened

#Testing

#Misc

#v0.105.0

GitHub ↗

Prism v0.105.0 is a consolidation release: harden the core and shrink it. It refactors large parts of the parser for speed and maintainability, and closes control-flow safety holes.

#Fixed

#Changed

#Removed

#Testing

#v0.100.0

GitHub ↗

First single-pass version of Prism. Unsafe early prototype; do not use.