Repository navigation
Releases
v0.9.2
Tycho 0.9.2 — pre-1.0, no stability guarantees.
0.9.2 expands the core library to cover the Odin core capability set and adds
Android and iOS compiler target gates. This is capability parity, not API parity:
Tycho keeps its value semantics, implicit arenas and Result model, so Odin
code does not compile unchanged.
Core library
- Compression — PPMd7/PPMd8, PKZIP Shrink/Reduce/Implode, Deflate64,
shoco, BCJ2, delta, BCJ filters for x86/ARM/ARM Thumb/ARM64/PowerPC/SPARC/
IA64/RISC-V, and swap2/swap4. - Encoding and assets — HxA v3 reader/writer, BNG decode/encode, Qt
.qm
catalogs, locale data and native Lua%b/%f/non-greedy patterns. - Platform and runtime — leap-second TZif records, epoll/kqueue/IOCP
completion loops, deterministic prof sampling with Spall traces, terminal
cursor queries and Windows tty fallbacks, thread-local storage and a
synchronous OS-thread runner. - C interop — typed C function-pointer invocation in
core:dynlib, and
named C-to-Tycho callbacks through compiler-generated C-ABI trampolines.
PPMd and the PKZIP legacy codecs are vendored public-domain C (7-Zip SDK and
hwzip). Deflate64 uses zlib's infback9. BNG uses the Unlicense reference
codec. The vendored codecs add no runtime library dependency.
Platforms
Prebuilt archives are attached for Linux x86-64, Windows x86-64 and
macOS arm64. The macOS archive is built natively on Apple Silicon and
smoke-tested there; it is not a cross-compiled binary.
Android and iOS are compiler target gates, not end-user platforms in
0.9.2. make android-check and make ios-check build and run dependency-free
corelib tests for those targets; no app archive, APK, Xcode project, JNI binding
or signed-device run is shipped.
Known limitations
- C-to-Tycho callbacks accept named functions only. Callback parameters and
results are limited to scalars,ptrandvoid; closures and function
values are rejected. mem,relative, runtimereflect, rawsys,c,odin,rexcode, SIMD
intrinsics andsliceremain explicit non-goals.- Tycho is pre-1.0. The language surface and corelib API may change before 1.0.
Prebuilt archives and checksums are attached to this release.
v0.9.1
Tycho 0.9.1 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
0.9.1 is the single-compiler, tooling and measured-performance checkpoint.
The language surface is unchanged. The historical C compiler is gone, the
self-hosted compiler is the only implementation, user-program builds are
substantially faster, and the remaining benchmark losses were closed or reduced.
One compiler
src/tychoc.cis deleted. Its final source remains in git history at
c7d46528ac6efcf9dc93359d14d98bedcb7ddbad. Historical citations resolve
against that revision; the reject classifier reads the frozen
compiler/reject_sites.json;single-compiler-checkfails if the file
reappears.tycho-debug btis source-aware. Demangledpkg.nameframes,.ty
file:line, with runtime glue and native frames tagged.
Compile speed
- Optimized user builds use LTO.
hello.tydrops from ~1.6 s to ~0.53 s;
compiler/main.tyfrom ~26.6 s to ~6.3 s on gcc 16.2.1. Generated units under
1 MiB use-flto; the compiler unit uses-flto=auto. Debug builds
(-O0 -g) keep the plain path. - The development loop is explicit.
make check-fastruns the fixture,
parser, citation, corelib, debugger, surface and doc-count gates in ~3 min;
make parse-checkis a real target and is in CI.
Performance
Large-size rows, same checksum per workload:
| row | 0.9.0 / before | 0.9.1 | C |
|---|---|---|---|
json_tree time |
84 ms | 51 ms | 49 ms |
json_tree RSS |
80.9 MB | 81.7 MB | 103.6 MB |
lru RSS |
20.2 MB | 14.7 MB | 12.0 MB |
lru time |
244 ms | 226 ms | 233 ms |
trie time (GCC) |
116 ms | 89 ms | 79 ms |
dispatch expert |
~24 ms | 12–13 ms | 13 ms |
binary/arena (GCC) |
423 ms | 377 ms | 292 ms |
binary/arena (clang) |
— | 516 ms | 504 ms |
The wins are structural: inlined string byte/equality paths with out-of-line
slow paths, thin 8-byte function values for programs that cannot create
closures, NULL-leaf recursive two-variant enums, small-map linear scans, and
map compaction at 1/8 dead. The remaining lru memory gap is the generic
map's duplicate key/value/index arrays; binary/arena is at clang parity and
still spends GCC cycles in recursive arena allocation.
Corelib
- Checked siblings for the last sentinel APIs:
io.write_text,
io.write_lines_checked,io.flush_stdout_checked,
net.udp_{bind,send,read}_checked,path.safe_join_checkedand
path.resolve_under_checked. - UDP read distinguishes a legal zero-length datagram from timeout and failure.
- All additions are recorded in
surface.lock; no corelib function was removed.
Verification
On the 0.9.1 tree:
make ci N=0— green, recorded for the exact treemake test— 1,157 fixture jobs, 0 failedmake corelib— 49 test trees, all greenmake fuzz N=200— 200/200make fuzz-reject N=200— 200/200make fuzz-leak N=150— 150/150make fuzz-tsan N=200— 63 ran, 137 skipped by design, 0 failedmake bench-suite-gate— green on GCC and clang resultsmake release-check— byte-identical native archivesscripts/release.sh v0.9.1 --mingw— Wine smoke-tested Windows archive
0.9.1 is pre-1.0 and there are no stability guarantees: anything here may
change before 1.0. The two 1.0 blockers are still a real program written by
someone other than the author, and an external security review; see
docs/internals/1.0-readiness.md.
v0.9.0: Keep borrowed heap values out of fresh push adoption
Tycho 0.9.0 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
0.9.0 is the managed-performance and compiler-parity checkpoint. The
language surface is unchanged; this release makes Tycho competitive with the
best managed implementations by measurement, closes the tracked gap between the
C bootstrap and the self-hosted compiler, and removes the remaining CLI hash
bottlenecks.
Managed performance
The generated suite covers 18 workloads at three sizes, with Go, Java and
Swift ports where the workload is meaningful. The release gate requires every
row to be within 1.10x of the best managed time or memory on at least one
backend, with a 1.15x self-regression limit and a repetition-noise check.
C and Rust are reported as native diagnostics, not release blockers.
Using large-size geometric means under GCC, Tycho measures 0.40x Go time and
0.50x Go memory; against Java the figures are 0.45x time and 0.05x
memory, and against Swift 0.18x time and 0.51x memory. The clang
report is similar (0.37x Go time, 0.50x Go memory). All compared rows pass the
managed gate.
The suite also gained specialized rows and managed ports:
- specialized
trie,dijkstra,lru,interpandjson_treerows now have
Java and Swift peers; - Rust/C service, SQLite, latency and specialized rows are measured for
context; binary/arena,dispatch/expertandmaptree/arenaremain explicit control
variants without managed ports.
Memory-heavy rows improved substantially:
| workload | before | 0.9.0 |
|---|---|---|
trie |
132.7 MB | 76 MB |
json_tree |
107 MB | 81 MB |
gcscan |
372 MB | 127 MB |
invindex growth rows |
— | 150–194 MB |
Compiler and runtime
- Reference-compiler parity is complete for the tracked features. The C
bootstrap now has the closure ABI, simple-place calls, counted/while
presizing, loop-scope elision, inline scalar-payload enums and nested push
destination inference. Its generated suite is checksum-clean. The saved
benchmark reports still measure the self-hostedtychoc1as the optimized
path. - Inline scalar-payload enums are emitted as a packed tag plus variant
union in both compilers instead of a pointer to an arena cell. Recursive,
generic, fieldless, package-mangled and heap-payload enums keep the pointer
layout.TYCHOC_NO_INLINE_ENUM=1restores the old representation for
measurement. - Closure environments and call descriptors are inlined for small
functions; loop-local arrays move into returned push destinations; counted
forand canonicalwhilebuilders are presized from their bounds. These
remove geometric growth and closure-scope allocations from the hot rows. - Map and list churn is now competitive: read-only map lookups borrow,
unshared map writes avoid a copy, integerpushhas an inline fast path, and
integer map keys use a collision-free permutation hash. Thewinaggrow
dropped from ~165 ms to ~84 ms against C ~97 ms. - JSON and map layout became flatter: composite map headers are smaller and
backed by a nullable data block; string map keys and repeated JSON keys are
interned; the fast parser builds a compact field spine directly; repeated
key/value strings are sliced once.Json.JObjnow carries a single
[JsonField]array; see Compatibility.
CLI and real programs
tycho-hashno longer deadlocks or grows with the file count. A producer
task feeds bounded 32-slot queues while the main thread drains results, which
preserves the 96-file stress fix without scaling memory with the tree size.- Whole-file hashing is hardware-accelerated when available. The runtime
uses SHA-NI first, optional OpenSSLSHA256next, and scalar C otherwise.
The pure Tycho streaming implementation remains the portable API. - On a deterministic 512-file / 128 MB tree with eight workers, Tycho now
hashes in 0.01 s / 10.8 MB versus Go's 0.01 s / 7.1 MB, down from
74.7 MB before the channel fix. - The production multi-worker server serves static files at 118.8k
requests/s / 3.5 MB, versus Gohttp.FileServerat 84.9k / 17.7 MB.
The minimal single-loop fixture is intentionally not the recommended server
shape; it does not scale with connections. - The real-program pass also covers concurrency rows, SQLite (
dbquerywithin
~2% of C and ahead of the managed ports), and large JSON documents (tied with
Java on time and far below it on memory).
Compatibility
No language syntax or value-model change. surface.lock gained only the
additive sha256.hex_bytes. The one known breaking corelib change is
corelib/json's object representation:
- before:
JObj([string], [Json])with parallel key/value arrays; - now:
JObj([JsonField]), whereJsonFieldholdsk: stringandv: Json.
Code that constructs or pattern-matches JObj directly must be updated. Code
that uses the parser, serializer and accessors is unaffected. sha256.hex_bytes
is additive.
Verification
make test— 1163 passedmake corelib— 49 okmake fixpoint-check—gen2.c == gen3.cmake bench-suite-gate— every compared managed row within 1.10xmake external-check— deterministic hash reports, worker counts and error
pathsmake docs-fences— 245 snippets, 0 failuresmake fuzz-quick— 60 seeds across native and ASan/UBSan builds, 0 failuresmake asan-self— 1171 compiler programs, 0 failures- GCC and clang backend reports; Windows cross-build check
Known gaps
- CLI hash process memory is ~1.5x Go's. SHA throughput is no longer the
cost; the remaining working set is the per-worker file buffer and runtime
state. - Minimal single-loop server scaling remains a documented limitation. Use
the production multi-worker server. - Native C/Rust still lead on some pointer-heavy or recursive workloads,
most visiblyjson_tree,trieanddijkstra. This is a diagnostic, not a
managed-language loss.
0.9.0 is pre-1.0 and there are no stability guarantees: anything here may
change before 1.0. See CHANGELOG.md for the accumulating record.
v0.8.9
Tycho 0.8.9 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
0.8.9 is the 3D and first-GPU checkpoint. The value model and the
O1–O7/FFI work from 0.8.8 are unchanged; this release adds a pure software-3D
library, three 3D callers, and an explicitly experimental SDL_GPU backend.
Software 3D
core:gfx3dis a pure Tycho camera and flat-shaded triangle rasterizer.
It exposesVec3,Camera,Target,SVert,Tri,MeshandPlace;
look_at,project,draw_tri,draw_quad,draw_box,clear,mesh,
add_tri,add_quad,add_boxanddraw_mesh.- Near-plane clipping and corrected barycentric inverse-depth interpolation are
pinned bycorelib/test/gfx3d, including the depth-ordering case that broke
the first cube renderer. Meshholds local-space triangles;Placeapplies a caller-built
right/up/forward basis, origin and uniform scale without copying the mesh.
Three callers
- Vector Vault — first-person collectathon, z-buffered triangles, portal,
timer, pause/restart, resize and a standalonedist/package. - Orbital Gallery — orbit camera and gallery scene, exercising a different
camera model. - Rotor Run — third-person drone game using
Mesh/Place, a multi-part
body and independently spinning rotor, follow camera and pickups.
All three use the same core:gfx window/input/audio/font path, and the software
renderer remains available.
GPU backend — experimental
core:gfxgpucreates an SDL_GPU device, pipeline, depth target and
swapchain for acore:gfxwindow.- Shaders are committed SPIR-V and translated to the native format by
SDL_shadercross (scripts/build-shadercross.shbuilds the pinned runtime). - Vector Vault and Rotor Run upload world-space meshes, use a camera MVP vertex
uniform, draw depth-tested scenes, and blend font-atlas HUD overlays directly
on the swapchain. The same code has run on Linux/Vulkan and macOS/Metal. - The backend is deliberately marked experimental: the shadercross runtime
is a separate build dependency, Windows GPU validation is not complete, and
at 480×270 internal resolution it is not yet faster than the software path.
Software remains the fallback.
Compatibility
No new value-model or escape-hatch language change. surface.lock moved by
addition only. The strings.parse_int, eprintln, path.real and FFI changes
from 0.8.8 remain in force.
Verification
make corelib— 49 ok.make parse-check— 1528 files.make surface-check— 486 corelib functions.- Three software 3D callers and the GPU probe pass headless/X11 checks.
- Valgrind and ASan are clean on the software callers and the gfx/image smoke
tests. GPU-driver leak coverage is not claimed on Linux. - The full local CI and release archive checks are run before tagging.
0.8.9 is pre-1.0 and there are no stability guarantees: anything here may
change before 1.0. See CHANGELOG.md for the accumulating record.
v0.8.8
Tycho 0.8.8 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
This release combines the seven pieces of real-program feedback from the
tycho-dupe probe with the O1–O7 optimizer sweep, POD struct FFI, and the first
complete game path. It adds no references and no escape hatches. The language
surface is unchanged in shape; surface.lock moved by addition only.
Breaking
strings.parse_intis now the strict parser. It returns
Result(int, IntErr)and refuses empty input, trailing garbage and
out-of-range values. The old fail-open scanner is now
strings.parse_int_prefix;parse_int_checkedis gone. Every call site,
test, example, tool and doc was updated. This is the one change a 0.8.7
program is likely to notice.eprintln(s)is a builtin. It writessand a trailing newline to
stderr.eprint(s + "\n")still works.path.realis implemented throughreal_checked. Its fail-closed
behavior is unchanged; the new spelling returns a namedPathErr.
What the probe changed
core:iocan now classify a file.FileKind,Stat,stat,lstat
andsame_file(device + inode on POSIX, handle identity on Windows) give a
filesystem tool one syscall for kind, size, mtime and identity. Symlinks,
hard links, FIFOs, sockets and devices now have answers instead of a C shim.core:pathcan say why a resolution failed.real_checkedreturns
NotFound/Denied/TooLong/BadPath/Failed;realremains the
fail-closed convenience.path.quotegives a POSIX shell-safe single-quoted path for text reports.tycho-dupeis intools/tycho-dupe, withmake dupe-check. It is the
probe program, not a demo: content hashing, filesystem identity, JSON output,
error paths and both compilers.- The copy/borrow boundary is written down. The reference, guide and spec
now say exactly which values are borrowed and which copy: heap descriptors
share storage;[N]T,vector[N]Tand inline-array structs copy the whole
value. inoutforwarding is documented with a compiling example.tools/tycho-dupe/FRICTION-OUTSIDE.mdrecords what went right and what
remains, with status notes for each finding.
FFI
Plain scalar-field structs can be inout out-parameters. A non-packed,
non-aligned struct of numeric fields now crosses as a normal S_Name *; the C
function fills it in place. Use fixed-width fields (i32, u8, f32) to match
the C header. The same POD struct can cross by value and as a return;
a dynamic [POD] parameter crosses as (const S_Name *, len), borrowed for the
call. This covers SDL_Rect/SDL_FRect/Vector2-style APIs without shimming the
values themselves. Strings, maps, handles, packed/align structs and non-POD
composites remain rejected — use --shim for those. tests/ffi covers the round
trips and all rejection paths. The windowed examples/gfx demo passes
SDL_FRect and SDL_Rect by value and a [FVec] (SDL_FPoint layout) array
to SDL_RenderLines, exercising the real SDL layouts on the GPU path. C unions
stay non-crossable; the shim decodes SDL_Event into a tagged POD Event.
The same example queues signed-16-bit PCM through an SDL audio stream, so sound
needs no callback ABI; SDL_AUDIODRIVER=dummy makes the test lane silent. Its
asset manifest (assets/manifest.json) is parsed with core:json; each PNG is
decoded with core:image, uploaded as an SDL texture, and looked up through a
[string: int] ID cache. One scoped TexSet handle owns every texture;
close(texset), close(audio) and close(win) release the textures, stream
and window in order. core:image frees its per-image handle header and keys
the double-free guard by a monotonic live-set ID, so loading the PNG atlas does
not retain one header per decode. Individual typed handles still cannot
live in collections, but a collection handle covers the homogeneous cache. A PNG glyph atlas is decoded by core:image, uploaded as an SDL texture, and
drawn glyph-by-glyph for SCORE n and GAME OVER, with no SDL_ttf. The reusable
layer now lives in core:gfx (corelib/gfx, auto-linked shim); examples/gfx/pong
is a second caller using the same package alongside Snake. present is
parameterless, with overlays drawn through the separate draw_lines call;
key-to-action mapping and centered text measurement live in core:gfx. A
gfx.Clock provides fixed-step simulation with a capped catch-up, so both games
simulate by time rather than frame count. Value Sprite/Anim types and
draw_sprite/anim_frame/draw_anim add sprite-sheet animation; the examples
use four-frame apple and ball sheets. A value Mixer with mixer/add/
finish/play_mix mixes overlapping PCM in Tycho; both games play a two-note
score jingle without audio callbacks. repeat/play_loop queue finite PCM
loops for music; the caller re-queues when it wants more. Snake now has a title
menu, three speeds, pause/restart, a persistent high score and letterboxed
resize, with package.sh producing a runnable directory.
Compiler optimizations
O1 ships in 0.8.8: immortal allocation + literal lifetime elision. String
literals now carry a negative length header; the runtime masks it, returns an
immortal literal unchanged on copy, and skips it on recycle. Every copy, push
and assign path inherits the elision without a compiler branch.
On 6,000,000 literal pushes into a [string] field: 207,460 KB / 61 ms →
114,056 KB / 28 ms (median of five), byte-identical output. The result is in
bench/opt/RESULTS.md.
Making O1 real also exposed a latent escape-analysis bug: return Env(m) now
marks m as escaping in both compilers, so a mutated map/array local returned
through a zero-cost newtype no longer keeps its storage in a scope that is
freed on the way out. tests/newtype_agg is the regression.
The O1–O7 optimization phases are all in 0.8.8; O1 was the prerequisite for
O6, and O7 closes the allocation gap on the flat array pipeline.
O2: read-only pass-by-pointer in the reference compiler. It passes
non-heap fixed arrays of four or more elements as const T* on direct calls.
On 5,000,000 calls through fn first(xs: [256]int): 140 ms → 66 ms (median
of ten, -O2 -fno-inline), same RSS. Function values, lambdas and spawns
bridge the ABI; FFI, inout and sink are unchanged.
The native archive ships tychoc1, the self-hosted compiler. It already
lowers [N]T to a 24-byte descriptor, so it never copied the 2 KB payload:
the same fixture measures 10 ms with the packaged compiler. O2 therefore
closes the reference compiler's ABI-copy gap and brings ./tychoc to the
representation the shipped compiler already had; the packaged binary's
performance is unchanged.
O3: last-use moves into push. A loop-local declared at the current loop
depth is a fresh buffer each iteration. When its only read is push(dst, x),
the declaration is promoted into the destination array's arena and the push
stores it without a deep copy; a heap rebind and a direct sink call get the
same relaxation. A local from outside the loop, or a push into a caller-owned
sink array, still copies. On bench/opt/movebuild (500,000 four-string loop
locals): tychoc1 91 ms → 79 ms, ./tychoc 96 ms → 87 ms, same RSS.
tests/move_loop_local.ty is the regression for both sides of the boundary.
O4: borrowed in-scope slices. A string slice used as a call argument is a
16-byte (parent, length) view instead of a copy. Binding, returning, push,
map keys and FFI still materialize through the copy seam. On 100 slices of a
12.8 MB string: tychoc1 33 ms / 42.9 MB → 7 ms / 30.6 MB, ./tychoc
32 ms / 43.0 MB → 6 ms / 30.7 MB. tests/pkg/slice_view covers both the
view and the materialization sides.
O5: destination passing for t = f(t, ...). When t is read once in the
RHS and immediately rebound, both compilers generate a variant of f whose
matching parameter is owned by the callee. Inside a return the variant may
move a field or match payload of the old value into the result instead of
deep-copying it, and a recursive call that consumes the changed child selects
the same variant. The result is built in the assignment target's arena, so a
moved field and the result share a lifetime; a call whose argument is still
live, or whose result goes anywhere else, keeps the copying function.
TYCHOC_NO_DEST_PASS=1 is the control.
A 12,000-insert binary tree drops from 2,252,644 KB / 500 ms to 8,908 KB /
3 ms in tychoc1 (./tychoc: 2,252,484 KB / 493 ms → 9,112 KB / 3 ms). A
16,384-leaf editor rope edited 10,000 times through t = set(t, i, v) drops
from 8,966,776 KB / 1,800 ms to 7,584 KB / 3 ms (./tychoc: 8,966,596 KB /
1,783 ms → 7,748 KB / 4 ms). Output is byte-identical. tests/dest_pass.ty
pins the unique, shared and snapshot cases; the fixtures are under
bench/opt/treeupdate and bench/opt/editorupdate.
O6: conservative shared-bit CoW / path copying. A same-arena b := a of a
composite map, or of a struct whose mutable fields are maps, shares the map and
sets a conservative shared bit; the first write through either copy duplicates
the map's arrays and marks nes...
v0.8.7
Tycho 0.8.7 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
This is a correctness release: it refuses programs 0.8.6 accepted, and
closes the 90-finding defect hunt of 2026-09-25. Every refusal names the
rule it enforces, and almost all of them are programs that were already wrong:
they wrapped a value silently, emitted C that did not build, or read memory
they should not have. There are no language features and no surface removals;
surface.lock moved by addition only (seven corelib functions joined it).
Programs now refused
- An out-of-range literal wherever it adapts.
x + 300at au8, a300
passed to a variadic or UFCS parameter, a lambda body, am.getdefault, a
subscript assignment, a valuematcharm, asend, or the broadcast scalar
of a sized-int array wrapped silently and printed the wrapped value. Every
site now says300 does not fit u8 (its range is 0..255). - An empty literal
[]for a[N]Torvector[N]T. The reference
compiler emitted an uninitialised C aggregate; the self-hosted one built a
length-0 "fixed" array. Now:a fixed-size array of length 2 needs 2 elements, got 0. - A
[N]TwhoseNis anintconst losing its length in the
self-hosted compiler: wrong-arity literals,pushonto it, zero or negative
N, and mismatched fixed-array arguments all compiled and ran with the
wronglen. - An f-string hole holding more than one expression.
f"{price:.2f}"
parsed the first expression, discarded the rest, and printed the bare value. - The identifier prefix
Ztycho_. Compiler temporaries shared the user
namespace, so a local named_fc0was silently replaced by the compiler's
own and the program printed a wrong answer. The prefix is reserved now. - A handle
free:destructor returning an aggregate, which passed the
check and failed later insidecc. - An empty
-o ""or a trailing bare-L/-Ion the compiler command
line.
Fixed
- The defect hunt is closed: 90 findings, all fixed. Eleven finder agents
on one surface each, every finding handed to a verifier told to refute it.
The ledger is
docs/internals/audit-2026-09-25.md.
Beyond the refusals above, the compiler half:- Division by zero traps cleanly in the self-hosted compiler instead of
SIGFPE/SIGILL, on every sized integer and element-wise array arithmetic. - An
if/elifchain and a long+chain emit flat. The self-hosted
compiler needed 5.3 GB for 800 arms and died at 1250; the reference
compiler segfaulted near 7000 arms and clang refuses 256 bracket levels.
A 1400-term sum now emits at depth 4 from both compilers. - Compile time on nested calls is no longer exponential: nested generic
method chains, 900-structprogram_incopies, one long f-string, lambdas
among many locals, and the reference front end's quadratic lookup. - A constant array literal is a static table plus
memcpy, not one
store per element — a 15,625-element table took gcc 21 s. - Enum variants named
EOF,NULL,defaultorwhilebuild, under
prefixed C names. - A
packedfield passed asinoutgoes through an aligned local in
both emitters, and no longer draws a-Wunused-valuewarning in the
user's build. - Composite float map keys hash
+0.0and-0.0alike; a map held two
==-equal keys. - The reference compiler's heap use-after-free on nested generic
instantiation is gone, withread_fileon pipes, the error-recovery
depth counter,__type names,clampnaming,--symbolson invalid
programs, and cross-package suggestions fixed alongside.
- Division by zero traps cleanly in the self-hosted compiler instead of
- Appending through an
inout stringin a helper no longer falls back to
quadratic concatenation. Both emitters pass a per-binding append state
that grows in the owner's arena (tests/inout_append.shbounds the growth).
The self-hosted compiler's redundant AST staging is gone with it. - Size arithmetic on untrusted counts is checked before it allocates:
reserveon composite arrays andchannel(T, cap)with a huge capacity
could wrap to a tiny allocation and write past it. Both abort by name now. - Overwriting a heap value in a long-lived map reclaims the old value.
tycho-kvsrvgrew ~1 KB of RSS perPUTto the same key without limit. - Enum payloads recycle at their exact size, not
sizeof(*v), in both
emitters. - Negative literals adapt like positive ones in the self-hosted compiler
(x: f32 = -0.5compiled under one compiler and not the other); float
to_u8..to_i64are total; au64shift past 2^63 is zero per spec. - The shipped compiler calls itself
tychocinstead oftychoc1, says
cannot open <path>for a missing source, and writes--emit-coutput to
stdout with no-o. core:tlsverifies the hostname withX509_VERIFY_PARAM_set1_host
rather than the deprecated call, andcore:datetimebounds the libc TZ
fallback on macOS.
Install
Download the tarball for your platform below, verify it, and unpack it:
tar xzf tycho-<version>-<os>-<arch>.tar.gz
cd tycho-<version>-<os>-<arch>
./tychoc examples/hello.ty && ./examples/hello
tychoc writes the binary beside its source unless you pass -o, so the
program built from examples/hello.ty is examples/hello.
The core library ships inside the tarball, beside the compiler, so there is
nothing to configure. You still need a C compiler (cc) on your PATH, because
Tycho transpiles to C: gcc, or clang 15 or newer. On macOS that is the Xcode
command line tools. Each tarball's SHA-256 is published alongside it.
What this release does not have
There has been no third-party security review, and the FFI
(foreign-function interface, extern fn) boundary is
unsafe by design. See SECURITY.md, and
docs/internals/audit-brief.md if you are
willing to be one.
Two things are unverified, and you may hit them:
- This Windows archive has been run under Wine, not on Windows. In a
debian:trixiecontainer, the stagedtychoc.exestarted under Wine and
emitted C for a program usingcore:stringsfound beside the binary. That
is the whole Windows verification: the 23-leg content gate ran over the
native Linux archive only. The toolchain itself has passed its suite on real
Windows guests, 289/289 on both windows-x86_64 and windows-arm64
(make platform-check), but that was an earlier build, not this tarball. - Server shutdown on Windows has never been observed on Windows. The code
reads sound: a worker re-reads the shutdown flag at least ten times a second,
and the console handler shuts down the listener and every registered
connection. What is untested is whetherWSAPollreturns early on a socket
that has been shut down, and whether a blockedacceptis released. Expect
wind-down within the 100 ms poll tick. Treat anything faster as unproven. - There is no
darwin-arm64archive this time. The 0.8.6 Mac archive
stands; this release attaches Linux and Windows only, both built from the
tagged tree. A Mac build needs a Mac.
Status
0.8.7 is pre-1.0 and there are no stability guarantees: anything here may
change. ROADMAP.md lists what 1.0 requires, and
the blocking item is not engineering. Nobody outside this repo has written a
real program in Tycho yet. If you write one, the friction you hit is the most
useful thing you can send back.
v0.8.6
Tycho 0.8.6 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
This release refuses programs 0.8.5 accepted, and removes 136 functions from
the public corelib surface. The list is below under Programs now refused.
Every refusal names the rule it enforces. Most of them are programs that were
already wrong: they failed later inside cc, leaked or double-freed a resource,
or read freed memory. Three are plain API changes: or_else is now a keyword,
the corelib helpers are private, and io.open_lines returns a Result.
macOS on Apple silicon
A darwin-arm64 archive is attached for the first time. Both compilers
build native Mach-O arm64 with no source change. The full make ci gate has
been green on macOS arm64 since 2026-09-19, and this archive is built twice and
compared byte for byte (make release-check), then extracted and checked
(make release-content).
Running the gate on macOS for the first time found defects that were not
specific to macOS. It was simply the first platform to show them:
- Every Tycho server refused connections under a burst on macOS.
net.listenasked for a backlog of 16. When the accept queue is full, Linux
drops the SYN and the client retries, while macOS and the BSDs reset the
connection. The backlog isSOMAXCONNnow. - A server could hang on shutdown, about one run in eight, because a worker
blocked inaccept()never saw SIGTERM. - Floating point could round differently on ARM. The emitted C left FMA
contraction to the C compiler. It fusesa*b + con aarch64 and cannot on
baseline x86-64, so the same program printed0.30000000000000006on one and
...004on the other. Contraction is off now, so a program rounds as its
source says on every host.
The binaries are not notarized by Apple. If macOS refuses to start them
after a browser download, clear the quarantine flag on the unpacked directory
with xattr -dr com.apple.quarantine tycho-v0.8.6-darwin-arm64.
New in the language
-
or_else: an early return that maps the error.cfg := load(path) or_else e: Err(ConfigErr(e)) ok := parse(s) or_else _: falseOn
Ok/Somethe expression is the payload, as withor_return. OnErr(e)
the function returns the handler. The handler has the enclosing
function's return type, which need not be aResult. It replaces the
four-linematchthat unwraps a value or returns a wrapped error. It is
refused in a function that returns nothing, because there is no value to
return; useor_returnor amatchthere. Spec §14.6.1. The tree's own code
moved to it at 79 call sites. -
size_of$(T)accepts analign(N)struct, soaligncan be observed
from inside the language. The size is implementation-defined and is
guaranteed to be a multiple ofN. -
math.min,math.maxandmath.clampwork lane by lane on a
vector[N]T, as one compare and blend.
The layout and SIMD surface itself (vector[N]T, align(N), packed,
swizzling) shipped in 0.8.5. This release adds its first in-tree user,
tools/tycho-grade.
Programs now refused
or_elseas an identifier. It is a keyword.- A call to a corelib internal helper. 136 functions such as
json.parse_valueandbignum.mag_cmpwere public only because their names
lacked a leading underscore. They are package-private now, and the error
names the private spelling. Every function found being called from outside its
package stayed public. The corelib surface is 423 functions, down from 559. io.open_linesreturning a nullableptr. It returns
Result(LineReader, IoErr). The null check becomes theErrarm.
CHANGELOG.mdshows the migration.reserveorpopon a fixed-size[N]Torvector[N]T. The reference
compiler emitted C that did not build. The shipped compiler acceptedpopon
a[4]intand silently shortened it.- Four misuses of a
handle:- a use after a
close(h)that closes it on every path; close(h)on a handle parameter, which freed the resource twice;- an opener whose result is not bound directly to a variable, which leaked it
for the life of the process; - a
free:that does not name anextern fntaking that handle type.
- a use after a
- An import one file declares and only a sibling file uses. It is reported
as unused in the file that declares it.
Fixed
- The shipped compiler returned freed memory in two shapes. An
or_return/or_elsepayload returned straight to the caller was built in an
arena the return had just freed.to_bytes(local)kept inside a returned
value pointed into the freed scope; incore:httpdthis corrupted a request
body. - The shipped compiler crashed on
x[a:b]over a struct or enum, with an
internal message and no line. It is a type error now. - A local
bounded[N]Tgrew past its capacity. It now traps on the push
that overflows it. core:tlsdid not guard an interior NUL in the host name. A string like
"good.example.com\0anything"connected to, announced and verified
good.example.com, while the caller's own checks saw the whole string. It
fails closed now.core:osrefuses to spawn a.bator.cmdon Windows.cmd.exe
re-parses the command line and defeats the argument quoting. A PATH-resolved
name with no extension is not caught.- Subnormal float literals became
0.0on aarch64 Windows, through mingw's
printf. - The vector-width lint gave x86-64 advice on ARM, a flag
ccthen refused.
It now suggests a narrower vector first. - A generic instantiation error names the whole chain of calls, starting
from the outermost one, which is the line you own. - Every build importing
core:stringsdrew two-Wunused-valuewarnings from
the reference compiler. - The build failed to link on Fedora with gcc 16:
-static-pieneeds-fPIE
beside it.
Install
Download the tarball for your platform below, verify it, and unpack it:
tar xzf tycho-<version>-<os>-<arch>.tar.gz
cd tycho-<version>-<os>-<arch>
./tychoc examples/hello.ty && ./examples/hello
tychoc writes the binary beside its source unless you pass -o, so the
program built from examples/hello.ty is examples/hello.
The core library ships inside the tarball, beside the compiler, so there is
nothing to configure. You still need a C compiler (cc) on your PATH, because
Tycho transpiles to C: gcc, or clang 15 or newer. On macOS that is the Xcode
command line tools. Each tarball's SHA-256 is published alongside it.
What this release does not have
There has been no third-party security review, and the FFI boundary is
unsafe by design. See SECURITY.md, and
docs/internals/audit-brief.md if you are
willing to be one.
Two things are unverified rather than known, because you may hit them:
- This Windows archive has been run under Wine, not on Windows. Its
content gate passed under Wine 10 on x86-64 Linux: every.exestarts, and a
real program is emitted, linked and run from a foreign directory. The
toolchain itself has passed its suite on real Windows guests, 289/289 on both
windows-x86_64 and windows-arm64 (make platform-check), but that was an
earlier build, not this tarball. - Server shutdown on Windows has never been observed on Windows. The code
reads sound: a worker re-reads the shutdown flag at least ten times a second,
and the console handler shuts down the listener and every registered
connection. What is untested is whetherWSAPollreturns early on a socket
that has been shut down, and whether a blockedacceptis released. Expect
wind-down within the 100 ms poll tick. Treat anything faster as unproven.
Status
0.8.6 is pre-1.0 and there are no stability guarantees: anything here may
change. ROADMAP.md lists what 1.0 requires, and
the blocking item is not engineering. Nobody outside this repo has written a
real program in Tycho yet. If you write one, the friction you hit is the most
useful thing you can send back.
v0.8.5: docs: name the host-compiler floor, and the three fixes, for 0.8.5
Tycho 0.8.5 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
This release refuses programs 0.8.0 accepted. Not a change of intent — those
programs were always invalid, and the shipped compiler failed to say so. A
survey of the C bootstrap found 495 user-facing rules, 262 of which no test in
the tree reached; the self-hosted compiler was missing a share of the rules it
had never been asked about. Around 270 rejection fixtures were written and the
gaps behind them closed. If your program stops compiling, the diagnostic names
the rule it broke. The two compilers now agree on every accept/reject verdict
across the whole corpus, and on which rule fired; the exact message text still
differs on 14 of 593 rejection fixtures.
Measured, not assumed: the 0.8.0 compiler was rebuilt and run beside this one
over the whole accept corpus — 420 programs, all 420 of which 0.8.0 compiled,
and all 420 still compile. Outside that corpus, three patterns 0.8.0 accepted
are now refused, and 0.8.0 did not merely accept them — it built and ran them:
push on a fixed-size array wrote past its storage, a slice of one read
storage that has no backing pointer, and an out-of-range u32 literal was
truncated to 0 in silence. A constant division overflow crashed the 0.8.0
compiler outright; it is a diagnostic now.
Windows finally works
Every executable in the 0.8.0 Windows archive fails to start. All four were
linked -pthread without -static and import a libwinpthread-1.dll the
archive does not carry. That archive is unusable and always was; this release is
the fix. The Windows tarball also now contains the self-hosted compiler
rather than the C bootstrap, which it shipped through 0.8.0 — worth about 3x
on a tree-walking benchmark. And tychoc.exe can be run from any directory: the
lookup for its own corelib split paths on / only, so with tycho on PATH
every build outside the archive directory failed.
The archive's contents are now gated, not just its bytes: it is extracted and
read on every check — the right compiler, the runtime present, no missing DLL
imports, every .exe starting, and a real program compiled and run from a
foreign directory.
All of that ran under Wine on Linux, not on Windows. No binary in this
release has been started on a real Windows kernel by anyone here.
Install
Download the tarball for your platform below, verify it, and unpack it:
tar xzf tycho-<version>-<os>-<arch>.tar.gz
cd tycho-<version>-<os>-<arch>
./tychoc examples/hello.ty && ./examples/hello
tychoc writes the binary beside its source unless you pass -o, so the
program built from examples/hello.ty is examples/hello.
The core library ships inside the tarball, beside the compiler, so there is
nothing to configure. You still need a C compiler (cc) on your PATH — Tycho
transpiles to C. Each tarball's SHA-256 is published alongside it.
New in the language
vector[N]T— a fixed array whose arithmetic is one machine instruction.
A power-of-two count from 2 to 64,int/float/f32elements. In 0.8.0 this
type existed in the documentation and not in the compiler that shipped: the
self-hosted compiler lowered it to a dynamic array and emitted no vector
instructions at all. It emits real ones now.--target <level>— raise the x86-64 baseline:x86-64-v2,-v3,-v4.
Worth knowing, because plain x86-64 is SSE2 and a register is 16 bytes, so a
32-bytevector[4]floatis split in half in every operation and can be slower
than the plain array it replaced. The compiler warns when that happens and
says which flag fixes it. Without the flag nothing changes: an unflagged build
is byte-identical to one made before the flag existed.align(N)andpacked— state a struct's layout.packedis byte-exact
with no padding;align(N)raises alignment, capped at 8 because that is what
the arena guarantees. A request the allocator cannot honour is refused at
compile time rather than rounded down in silence.- Simultaneous assignment —
(x, y) = (y, x). Targets are places, so a
field, an array element or a map value may stand on the left, and every
right-hand side is evaluated before any target is written.(a, b) = (b, a+b)
is one Fibonacci step. - Swizzling —
v.(x, y)is a tuple of two components of one value, and it
reads, binds and assigns. Fixed arrays and vectors of four lanes or fewer name
them.x .y .z .w, or.r .g .b .afor the same four, wherever a field
access is accepted. - A byte bridge —
to_bytes,from_bytes$(T)andsize_of$(T)over a
packed struct, little-endian on every host.
Fixed
- The example server starved under load. A worker served one connection
start to finish, so 64 idle peers delayed a request by 2080 ms, and 64 parked
keep-alive peers meant only four requests — the worker count — could be
answered at all. Rewritten around onepoll(2)per worker: 21–33 ms with all
64 answered, unchanged at 256. - A double free of a
core:crypto,core:tls,core:httporcore:image
handle segfaulted, and using one after free returned a garbage number and
exited 0. Both die by name now. - Float-to-integer conversions were undefined for a NaN, an infinity or an
out-of-range value. They are checked at run time. - The format parsers stopped failing open —
core:csv,core:json,
core:toml,core:cliandcore:markdown. makefailed on a fresh clone under Debian and Ubuntu. The runtime is
escaped into a C string literal by anawkrule, and mawk — the defaultawk
on both — halves a backslash where every other awk doubles it, so all 89
backslashes in the runtime reachedccunescaped and the build died at
\x used with no following hex digits. The rule no longer depends on which
awk is installed. Present since 0.5.0.vector[N]Tand swizzling did not compile under any clang. The lowering
took the address of a vector lane, which gcc permits and clang refuses in
every version. Five fixtures failed; the whole corpus now agrees between gcc
and clang 19. clang 15 or newer — clang 14 miscompiles the emitted C and
is not supported.--print-depsreported nothing forcore:sqlite, somake corelib
failed rather than skipped where libsqlite3 is absent. A package'sdepsfile
is now read whether or not it also ships a C shim.
Security
core:httprefusesfile://URLs — a URL from anywhere untrusted could read
a local file through the HTTP client.tycho-httpdrefuses ambiguous request framing (request smuggling), and the
example server refuses a symlink escape out of its document root.- Three sites interpolated attacker-shaped text into a shell command line; all
are quoted now. tycho-rsais gone. A pure-Tycho RSA cannot be constant-time — the
modular exponentiation leaks the key through timing — and padding it did not
change that. Usecore:crypto, which is OpenSSL.
The surface moved, deliberately
0.8.0 froze the keyword set, the builtin set and every corelib signature, and
said no new language features before 1.0. This release breaks that: the layout and
SIMD work above needed surface, and it was judged worth taking now rather than
after 1.0, when it could not be taken at all. The lock still exists and still
gates — 115 keywords, 41 builtins, 559 corelib functions — but it records what
was added instead of forbidding additions. Corelib may still gain a function and
may not lose one or change a signature.
What this release does not have
There has been no third-party security review, and the FFI boundary is
unsafe by design — see SECURITY.md, and
docs/internals/audit-brief.md if you are
willing to be one.
One thing is unverified rather than known, because you may hit it:
- Server shutdown on Windows has never been observed on Windows. The code
reads sound in both halves: a worker parks innet.wait_readablewith a tick
of at most 100 ms (server/main.ty@POLL_TICK_MS), so it re-reads the
shutdown flag ten times a second whether or not anything wakes it, and the
console handler shuts down the listener and every registered connection
(corelib/signal/signal_shim.c@sigx_ctrl_handler). What is untested is
whetherWSAPollreturns early on a socket that has been shut down, and
whether a blockedacceptis released; both are Windows-version dependent
and no lane here can reach a real Windows kernel. Expect wind-down within the
poll tick; treat anything faster as unproven.
Status
0.8.5 is pre-1.0 and there are no stability guarantees: anything here may
change. ROADMAP.md lists what 1.0 requires, and
the blocking item is not engineering — it is that nobody outside this repo has
written a real program in Tycho yet. If you write one, the friction you hit is
the most useful thing you can send back.
Tycho 0.8.0
Tycho 0.8 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
This release does not break source compatibility with 0.7. That is measured,
not assumed: the 0.7.0 compiler was rebuilt and run beside this one over the
whole accept corpus — 300 programs, 297 of which 0.7.0 compiled, all 297 still
compile, and three that 0.7.0 refused now work.
The compiler is now written in Tycho
tychoc in the Linux tarball is tychoc1, the self-hosted compiler. It is built
in two stages: the C bootstrap in src/tychoc.c builds stage 1, and stage 1
builds the compiler you get, so it carries its own optimisations. The bootstrap
is not going away — it is how a clean checkout gets started.
On Windows the tarball still contains the C bootstrap. The mingw64 build
cross-compiles src/tychoc.c and does not cross-build the self-hosted compiler.
Same language, same version, different implementation.
On its own source the self-hosted compiler now compiles faster than the
bootstrap — 73 ms against 105 ms, minimum of ten runs — and is level on the other
inputs measured. It started this cycle five times slower.
Install
Download the tarball for your platform below, verify it, and unpack it:
tar xzf tycho-<version>-<os>-<arch>.tar.gz
cd tycho-<version>-<os>-<arch>
./tychoc examples/hello.ty && ./examples/hello
tychoc writes the binary beside its source unless you pass -o, so the
program built from examples/hello.ty is examples/hello.
The core library ships inside the tarball, beside the compiler, so there is
nothing to configure. You still need a C compiler (cc) on your PATH — Tycho
transpiles to C. Each tarball's SHA-256 is published alongside it.
What's new
- Three generic shapes that 0.7 refused now compile: a generic function
returning an array under awhereclause, a generic constructor taking a bare
[], and a lambda argument at a generic call site. io.make_dir_all—mkdir -p, whichtycho-arhad hand-rolled — plus
io.append_text,io.copyandstrings.format_g17.- Array-literal codegen was quadratic. 30,000 elements went from over a
minute to 20 ms. - Three new tools:
tycho-diff,tycho-fold,tycho-hash. - Diagnostics: several messages named the compiler's internals rather than
the mistake, and two offered confident suggestions built on a wrong
assumption. Those are rewritten.
Deprecated
decimal.from_strfails open — it returned0.15for"1.5x"— and is
deprecated. Usedecimal.parsefor a checked read, or
decimal.parse_uncheckedif you really want the old behaviour. It still
works in 0.8 and warns.
The language surface is frozen
Since 2026-08-22 the keyword set, the builtin set and every corelib signature are
locked and gated: 101 keywords, 41 builtins, no additions or removals. Corelib
may gain a function; it may not lose one or change a signature. No new
language features before 1.0. The point is that the surface stops moving long
enough to be learned and depended on.
What this release does not have
There has been no third-party security review, and the FFI boundary is
unsafe by design — see SECURITY.md, and
docs/internals/audit-brief.md if you are
willing to be one.
One limit is stated rather than fixed, because you may hit it:
- On Windows, a server winds down within its idle timeout rather than
within a millisecond: a thread parked inrecvis not released by the
shutdown handler as it is on Linux. Nothing is lost or corrupted.
Status
0.8 is pre-1.0 and there are no stability guarantees: anything here may
change. ROADMAP.md lists what 1.0 requires, and
the blocking item is not engineering — it is that nobody outside this repo has
written a real program in Tycho yet. If you write one, the friction you hit is
the most useful thing you can send back.
Tycho 0.7.0
Tycho 0.7 — pre-1.0, no stability guarantees (see the README for
what that means in practice). Prebuilt binaries are attached, so you can try the
language without building from source.
This release breaks source compatibility with 0.6. Several shapes that
compiled before are now refused, and two of them were live memory errors rather
than style: a copied handle gave one pointer two owners and glibc reported
double free detected in tcache 2 from a four-line program, and a copied channel
aliased one channel to two owners so a send through one was received through the
other. Also refused now: sink/inout on an affine type, a bare handle as a
struct field, and a bounded[N]T generic field that had silently degraded to a
plain fixed array. JsonErr gains a variant, which breaks an exhaustive match
on it. Every migration is mechanical; CHANGELOG.md's breaking
sections state each as "what you wrote" → "what you write" and are the list, not
this paragraph.
Security fixes
core:crypto — two of these are credential-grade, and both failed by
collision rather than by weakness, which is the mode that does not look like
a bug:
- A password was truncated at its first NUL. The shim called
strlen, so
"secret\0A"and"secret\0B"derived the same key — and the same one as
"secret". Two distinct credentials authenticated against each other. The
length is passed explicitly now. ct_equalcompared two hex strings by their prefixes, for the same reason.
Not reachable in the MAC-verification shape — a computed MAC carries no NUL, so
the lengths disagreed and the answer was already false — but a caller comparing
two supplied values had a collision.- The plaintext was left in freed memory.
aead_encryptreleased the
plaintext it decoded andaead_decryptthe one it recovered, neither wiped.
The recycled hex return buffer also kept its last value — including a key from
key_export_hex— for the life of the thread. - Key import was not constant-time. The hex decode rejected at the first bad
digit, which times the offset of the error. Branch-free now, verified under
valgrind rather than by timing: 7 secret-dependent branches before, 0 after.
Elsewhere:
core:httpcan be pointed at a private CA (SSL_CERT_FILE/
SSL_CERT_DIR), which is what makes its certificate verification testable at
all. It was correct before and unprovable;make http-verifynow holds it.markdown.renderno longer emitsjavascript:ordata:hrefs, and
escapes attribute values as well as text.json.parse_checkedrefuses a document nested past 2000 instead of
aborting the process. A deeply nested document used to kill it with
tycho: stack overflow, so the one entry point with an error channel had a
hole exactly where an attacker controls the input.sqlitebound parameters survive an interior NUL, andtoml.parserefuses
a repeated[table]header that used to delete the first one.
There is still no third-party audit of any of this — see
SECURITY.md, and
docs/internals/audit-brief.md if you are willing
to be one.
Install
Download the tarball for your platform below, verify it, and unpack it:
tar xzf tycho-<version>-<os>-<arch>.tar.gz
cd tycho-<version>-<os>-<arch>
./tychoc examples/hello.ty && ./examples/hello
tychoc writes the binary beside its source unless you pass -o, so the
program built from examples/hello.ty is examples/hello.
The core library ships inside the tarball, beside the compiler, so there's nothing to
configure. You still need a C compiler (cc) on your PATH — Tycho transpiles to C. Each
tarball's SHA-256 is published alongside it.
Breaking changes
Full detail and migrations in CHANGELOG.md; the short form:
- A binding may not start with an uppercase letter. Locals, parameters,
loop variables and pattern bindings must start lowercase or_. The
uppercase namespace belongs to types, enum variants and consts, so a binding
can no longer shadow a constructor.constis exempt. Rename the binding. isis now a reserved keyword (the variant test, below). A variable or
function namedisno longer compiles.- A function may not be named
Ok,Err,Some,None, or after any
enum variant or const. Rename it. core:iterpredicates returnbool, notint—filter,try_filter,
count,any. Writefn(x: int) -> bool: x % 2 != 0where you wrote
fn(x: int) -> int: x % 2.image.decode/encodeandcompress.decompressreturn aResult
instead of an empty-or-sentinel value, so a corrupt input is no longer
indistinguishable from a legitimately empty one. Addor_returnor amatch.tycho-ar xexits 1 when an mtime could not be restored. It now finishes
the extraction and warns per member rather than dying part-way; exit 0 still
means fully restored.
What's new
is, the variant test —x is VariantNamefor an enum, anOptionor a
Result, so you can ask without destructuring:if r is Ok:,o is Some.- A
[string]may cross the FFI as an extern parameter, arriving in C as
(const char *const *, long). This retiredcore:os's whole argv-builder
protocol;os.execandos.exec_outare unchanged for callers. io.mtime/io.set_mtime,strings.slice_bytes/slice_str(a
fail-closed slice, where the built-in one clamps),iter.try_map/
try_filter(short-circuit on the firstErr),result.map_err_with
(translate an error and keep the cause), anddecimal.divwith rounding
modes and three named failure causes.- f-string interpolations evaluate left to right. They printed in source
order but called in reverse, because the holes became arguments to one C
function and C leaves argument order unspecified. Observable whenever a hole
carries a call with a side effect. - A compiler use-after-free is fixed. A stale pointer into the signature
table, dangling after a generic instantiation reallocated it, produced bogus
refusals of valid programs that came and went with unrelated edits. - Better diagnostics: a refusal inside a generic now names the call site
that instantiated it, and a non-exhaustiveResult/Optionmatch names the
uncovered variant instead of claiming a side was missing.
Two limits remain stated rather than fixed, because you will hit them:
core:nethas no readiness polling. Nopoll/select/epoll/
O_NONBLOCKanywhere in the package, so a server's worker count is a hard
ceiling on concurrent connections and one slow client occupies one worker.- On Windows, a server winds down within its idle timeout rather than
within a millisecond: a thread parked inrecvis not released by the
shutdown handler as it is on Linux. Nothing is lost or corrupted.
What this release does not have
Stated plainly, because the previous draft of these notes advertised the
opposite. Until 2026-07-29 every program was compiled by two independent
implementations — tychoc and the self-hosted tychoc0 — which had to agree.
That differential is retired and nothing replaces it. It caught real
defects, specifically changes that silently narrowed what the language accepts,
and that class of defect now has no second opinion. compiler/tychoc0.ty
remains in the tree as the artifact proving Tycho self-hosts, and as the
compiler's hardest sanitizer input, but it is no longer a check.
There has also been no third-party security review, and the FFI boundary is
unsafe by design — see SECURITY.md.
Status
0.7 is pre-1.0 and there are no stability guarantees: anything here may
change — this release broke source compatibility in six places and the next one
may too. ROADMAP.md lists what 1.0 requires, and
the blocking item is not engineering — it is that nobody outside this repo has
written a real program in Tycho yet. If you write one, the friction you hit is
the most useful thing you can send back.