Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,25 @@ jobs:
fi
done

- name: Check self-hosted lexer (compiler)
- name: Native compiler package (yard)
run: |
echo "Checking compiler package (yard)..."
scripts/embed_runtime.sh --check
(cd compiler && ../target/release/yard check)
echo "Building compiler..."
(cd compiler && ../target/release/yard build)
echo "Testing compiler (native lexer)..."
(cd compiler && ../target/release/yard test)
echo "Checking compiler sources via track..."
for f in compiler/src/*.trk; do
echo "Checking $f..."
./target/release/track check "$f"
done

- name: Bootstrap fixed point (stage0 -> stage3)
run: YARD=./target/release/yard scripts/bootstrap.sh

- name: Verify versions
run: |
./target/release/track --version | grep -q "0.7.0"
./target/release/yard --version | grep -q "0.7.0"
./target/release/track --version | grep -q "1.0.0"
./target/release/yard --version | grep -q "1.0.0"
./build/bootstrap/stage3/trackc --version | grep -q "1.0.0"
echo "✓ versions ok"


Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ scratch/benchmarks/branch
scratch/benchmarks/tuple
scratch/benchmarks/loop
scratch/benchmarks/rec
/build/
74 changes: 69 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,78 @@ All notable changes to the Track programming language and toolchain will be docu
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [1.0.0] — 2026-10-30

### Fixed
- Yard preserves source-relative paths under `target/objects/`, preventing nested modules with the same basename from overwriting each other or the runtime object.
- Parallel build failures, check diagnostics, dependency resolution, and build-cache serialization now use stable ordering. Cache-save errors are propagated.
Track is self-hosting. `trackc`, the compiler in `compiler/`, is written
entirely in Track and reaches a bootstrap fixed point: the Rust-built and
Track-built compilers emit byte-identical C, and the stage 2 and stage 3
executables are byte-identical (`scripts/bootstrap.sh`, gated in CI).

### Added
- Build-determinism regression coverage for duplicate basenames, runtime-name collisions, incremental reuse, clean-build metadata, and error ordering.
- **Self-hosted compiler** (`compiler/src/`): lexer, full-grammar parser,
whole-program module loader, ownership checker, and a portable C99 backend.
CLI: `trackc build | run | check | emit-c | tokens | --version`. Lexer,
parser, and checker errors print `error[Exxxx]` diagnostics with a source
frame. See [docs/bootstrap.md](docs/bootstrap.md).
- **Shared runtime** `runtime/track_runtime.c`: one word-ABI implementation
linked by both compilers (embedded into `trackc` by
`scripts/embed_runtime.sh`). New: string builder, whole-file read/write,
unformatted output, collection pop/truncate, `track_str_bytes`.
- `scripts/bootstrap.sh` (stage 0 → 3 with the fixed-point gate) and
`tests/selfhost_tests.rs`: fixed point, behavioural parity on every
example, identical accept/reject verdicts from both checkers, and the
native suite run under `trackc`.
- `tests/codegen_semantics_tests.rs`: run-and-compare regressions for the
backend fixes below.

- **Structs and arrays at run time** (both compilers): struct literals,
`value.field` reads, `p.x = v` / `t.0 = v` / `a[i] = v` updates (root must
be `let mut` or a lens alias), struct patterns that match by field,
bounds-checked array indexing, `a[s..e]` / `a[s..]` slices, and
`array_len`. Previously these parsed but evaluated to `0`. Unknown struct
fields are compile errors. `examples/structs_arrays.trk` demonstrates them.

### Fixed (Rust bootstrap compiler)
- **Word ABI in the Cranelift backend.** Values mixed I8/I32/I64 and calls to
functions in other modules were declared with guessed signatures, so a
`bool` returned across modules could carry garbage in its upper bits and
C `int` results lost their sign (`file_remove` returning `-1` read as
`4294967295`). Every value is now a 64-bit word.
- `os_args_count()`/`os_arg()` never saw the program arguments.
- `&&`/`||` were bitwise (`1 && 2` was false) and did not short-circuit.
- `return` inside a `match` arm, directly in a `while` body, or in nested
`if`s panicked the backend ("block already filled").
- `if`/`match` used as expressions read values from non-dominating blocks.
- Returning a local bound to a tuple literal returned a dangling stack slot.
- String literal patterns compared pointers instead of contents.
- Union payloads were lost (`Value::Int(42)` never matched `Value::Int(x)`);
payload unions are now `[tag, payload]` cells. Explicit enum
discriminants (`A = 5`) were ignored, and variant lookup used bare names,
so two enums sharing a variant name collided.
- Constants compiled to `0` when used from another module, and a constant
declared after the function using it was reported as undeclared.
- Variables declared inside `if`/`while`/`for`/`match`/`with` blocks leaked
into the enclosing scope, so shadowing changed the outer variable.
- `let mut x` made every later `x` mutable, in any function. Mutability is now
lexically scoped; parameters and loop variables are immutable.
- Assigning to an undeclared variable was silently accepted.
- `Vec` (a 16-byte struct return truncated to its data pointer) and `math_*`
(declared with `double` parameters, called with integers) returned garbage.
- `str_substr`/`str_char_at` scanned the whole string on every call.
- Open-ended slices (`a[2..]`) failed to parse.
- Generic calls inside index expressions and slice bounds were not
monomorphized.
- Yard reused cached objects after the compiler changed or after an imported
module's constants changed; the cache key now covers both.
- Yard preserves source-relative paths under `target/objects/`, preventing
nested modules with the same basename from overwriting each other or the
runtime object; build failures, check diagnostics, dependency resolution,
and build-cache serialization use stable ordering.

### Changed
- Version `1.0.0` for `track`, `yard`, `trackc`, and `compiler/Track.toml`.
- `compiler/src/token.trk` and the v0.8.0a subset parser/AST were replaced by
the full implementation; their tests were rewritten.

## [0.7.0] — 2026-08-23

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "track"
version = "0.7.0"
version = "1.0.0"
edition = "2024"

[[bin]]
Expand Down
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Track** is a low-level systems programming language designed for deterministic memory management, zero-cost abstractions, and real-time execution.

- **No Garbage Collector, No Runtime.** Resource lifecycles are verified and freed at compile time.
- **No Garbage Collector.** Resource lifecycles are verified at compile time; there is no collector and only a small C runtime.
- **Linear Ownership.** Eliminates use-after-free, double-free, and memory leaks.
- **Lexical Lenses.** Scoped mutable access without manual pointer arithmetic or ownership transfer.
- **Cranelift Standalone Backend.** Fast native code generation without system LLVM dynamic library dependencies.
Expand All @@ -22,7 +22,7 @@ fn main() -> void {
```track
import "std/io";

// Linear ownership — freed automatically at spend point
// Linear ownership — the checker rejects use after a move
let mut v: Vec = vec_init(16);
vec_push(&mut v, 42);

Expand Down Expand Up @@ -96,24 +96,32 @@ yard run
yard test
```

### Self-Hosted Compiler (`compiler/`)
### Self-Hosted Compiler (`trackc`)

`compiler/` is a `yard` package that hosts the native Track lexer (`v0.7.0`):
Track is self-hosting: `compiler/` is the Track compiler written in Track.
It compiles itself to a verified fixed point.

```bash
# Check / build the self-hosted lexer
yard check # in compiler/
yard build # -> compiler/target/trackc
yard test # 10 lexer tests
# Build trackc with the Rust bootstrap compiler
(cd compiler && yard build) # -> compiler/target/trackc

# Dump tokens for a string (oracle vs src/lexer/mod.rs)
compiler/target/trackc "let x = 42;"
compiler/target/trackc build examples/hello.trk -o hello
compiler/target/trackc run examples/generics.trk
compiler/target/trackc emit-c examples/hello.trk # generated C on stdout

# Stage 0 -> 1 -> 2 -> 3, then check the fixed point
scripts/bootstrap.sh
```

See [docs/bootstrap.md](docs/bootstrap.md) for the pipeline, the shared
word-ABI runtime, and the C backend contract.

## Testing

```bash
cargo test
cargo test # Rust suites, incl. the self-hosting parity tests
(cd compiler && yard test) # native compiler tests
scripts/bootstrap.sh # bootstrap fixed point
```

## License
Expand Down
39 changes: 35 additions & 4 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Track Core Language Specification (v0.6)
# Track Core Language Specification (v1.0)

Track is a low-level systems programming language designed for deterministic memory safety without a garbage collector or complex lifetime annotations.

Expand Down Expand Up @@ -46,7 +46,7 @@ An **Owned Linear** value has exactly one owner at any point during execution.
A linear resource is consumed (spent) by:
1. **Move Assignment**: `let y = x;` transfers ownership from `x` to `y`. `x` transitions to state `Spent`.
2. **Function Call Transfer**: Passing an owned value into a function parameter transfers ownership to the callee.
3. **Implicit Scope Cleanup**: If a linear resource remains `Active` at scope exit, the compiler automatically emits cleanup deallocation code (`vec_free`, `str_free`).
3. **Scope Exit**: Manual frees (`free`, `str_free`, `vec_free`, `file_close`) are rejected, so a resource can never be freed twice or used after a free. In v1.0 neither compiler inserts deallocation code yet: heap memory owned by linear values is reclaimed when the process exits. Emitting frees at scope exit from the ownership analysis is planned.

### Struct Ownership & Field Move Policy
Structs in Track are moved as **atomic units**. Moving any field or the struct itself consumes the entire struct instance, preventing use-after-free or partial double-destruction.
Expand Down Expand Up @@ -99,7 +99,17 @@ A variable is usable after the loop only if the merged state is consistent acros

---

## 6. Anonymous Tuples & Destructuring
## 6. Structs, Arrays, Anonymous Tuples & Destructuring

- **Structs**: `struct Point { x: i64, y: i64 }` declares a linear record.
`Point { x: 1, y: 2 }` constructs one (unknown fields are rejected),
`p.x` reads a field, and `p.x = v` updates it when `p` is `let mut` or a
lens alias. Struct patterns `Point { x: 0, y }` match by field.
- **Arrays**: `[a, b, c]` creates a linear, fixed-length array. `a[i]` and
`a[i] = v` are bounds checked at run time (out-of-range access aborts),
`a[s..e]` and `a[s..]` copy a slice, and `array_len(&a)` gives the length.
- Tuple elements can be updated in place with `t.0 = v`.


Tuples are heterogeneous value containers specified with `(T1, T2, ...)`.

Expand Down Expand Up @@ -140,11 +150,32 @@ Generic functions are **templates** monomorphized at call sites.
- **Syntax**: `fn name<T, U>(x: T, y: U) -> T { ... }`.
- **Inference**: Type parameters are inferred from argument types (variable bindings, literals). Annotate `let x: i64 = ...` when inference is ambiguous.
- **Monomorphization**: Each distinct instantiation (`identity<i32>`, `identity<i64>`) produces a mangled concrete function (`identity__Ti32`). Call sites are rewritten; only instances are checked and emitted. No boxing, no runtime cost.
- **Self-hosted backend**: because every value is a machine word (§11), `trackc` emits one C function per generic template; it behaves exactly like the per-instance functions of the Rust compiler.
- **Limitations (v0.6.0)**: Generic functions are fully supported. Generic structs (`struct Stack<T>`) header-parsing is reserved for a follow-on; `Stack<T>` as a type argument is not yet monomorphized.

---

## 10. Diagnostics & Error Reporting
## 10. Mutability

Bindings are immutable unless declared `let mut` (or bound by a `let mut`
destructuring pattern). Mutability belongs to the binding: a later
`let x = ...` shadows a mutable `x` with an immutable one, and a binding goes
out of scope at the end of its block. Parameters, `for` loop variables, and
lens aliases are immutable. Assigning to an undeclared name is an error.

## 11. Runtime Value Model

Every value is one 64-bit word: integers and booleans directly (comparisons
and `&&`/`||` yield `0` or `1`; `&&`/`||` short-circuit), strings as
NUL-terminated byte pointers, references as the referenced value, tuples as
pointers to word arrays, enums as their discriminant, values of unions
with payload variants as heap cells `[tag, payload...]`, structs as
`[name hash, fields...]`, and arrays as `[length, elements...]`. Arithmetic wraps,
shift amounts are taken modulo 64, `>>` is logical, and integer division by
zero traps. Both compilers share this model and the runtime in
`runtime/track_runtime.c`; see [docs/bootstrap.md](docs/bootstrap.md).

## 12. Diagnostics & Error Reporting

Track compiler diagnostics report span location, root cause, and ownership state transitions:

Expand Down
2 changes: 1 addition & 1 deletion compiler/Track.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "trackc"
version = "0.7.0"
version = "1.0.0"
authors = []

[dependencies]
Expand Down
Loading
Loading