Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6138ba2
docs: defer WASM target after Cranelift spike outcome
artefactop May 20, 2026
4088621
docs: align wasm proposal with dev-doc conventions
artefactop May 20, 2026
e0dffaa
Merge remote-tracking branch 'origin' into design/milestone-8.1-spec
artefactop May 20, 2026
4930887
feat(runtime): workspace skeleton + alloc/free/realloc
artefactop May 20, 2026
f1bcb1a
feat(runtime): from_literal, concat, eq
artefactop May 20, 2026
3e50211
feat(runtime): int_to_str, float_to_str, bool_to_str formatters
artefactop May 20, 2026
e58882e
refactor(codegen): introduce ValueRepr enum for multi-value str
artefactop May 20, 2026
1b4f759
refactor(codegen): cranelift_type_for audit + is_str_type guard
artefactop May 20, 2026
688c378
feat(codegen): StrConst lowering via ryo_str_from_literal
artefactop May 20, 2026
4214d67
feat(linker): link libryo_runtime.a into every binary
artefactop May 20, 2026
526359e
feat(codegen): print() accepts heap-allocated str values
artefactop May 20, 2026
e1871f9
feat: str + str concatenation via ryo_str_concat
artefactop May 20, 2026
9457310
feat: str == / != equality via ryo_str_eq
artefactop May 20, 2026
7270ef7
feat: int_to_str, float_to_str, bool_to_str builtins
artefactop May 20, 2026
99189ff
feat(parser): dot-method-call syntax (expr.name(args))
artefactop May 20, 2026
2eb7b10
feat: .len() and .is_empty() method calls on str
artefactop May 21, 2026
00a3b36
test: comprehensive str integration tests + edge cases
artefactop May 21, 2026
c78fa06
feat(codegen): str function parameters and returns via sret
artefactop May 21, 2026
ace8934
fix: runtime correctness bugs and dead code cleanup
artefactop May 21, 2026
d67ddac
docs: update roadmap ownership lattice and CLAUDE.md emphasis
artefactop May 21, 2026
6fb04f4
perf(runtime): static string optimization — literals avoid heap alloc…
artefactop May 21, 2026
46da72a
test(runtime): verify static/heap string interaction safety
artefactop May 21, 2026
103b96b
fix(runtime): replace naive float_to_str with ryu for correctness
artefactop May 21, 2026
b065a59
perf(build): cache runtime archive with content hash
artefactop May 21, 2026
82ffee5
test: integration tests for float_to_str edge cases
artefactop May 21, 2026
af4079a
style: cargo fmt
artefactop May 21, 2026
dd3a335
fix(ci): build runtime before clippy and test
artefactop May 21, 2026
50814c9
fix: address code review findings across runtime, codegen, and sema
artefactop May 21, 2026
51608f4
fix(linker): link libunwind on Linux to resolve _Unwind_* symbols
artefactop May 21, 2026
903410b
docs: add I-043 (runtime no_std migration) and issue tracking convention
artefactop May 21, 2026
957d43e
fix: pass runtime path as OsStr and validate u64→usize in str_concat
artefactop May 21, 2026
5fa2ed4
fix: implement PR #74 review comments & auto-bootstrap runtime
artefactop Jun 3, 2026
eaabab1
fix: resolve clippy warnings in build.rs and format workspace
artefactop Jun 3, 2026
488487c
fix(build): avoid string panics in build.rs & document profile logic
artefactop Jun 3, 2026
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
prefix-key: clippy
- run: cargo build -p ryo-runtime --release
- run: cargo clippy --all-targets

test:
Expand Down Expand Up @@ -65,4 +66,5 @@ jobs:
- name: Install Zig toolchain
if: steps.zig-cache.outputs.cache-hit != 'true'
run: cargo run -- toolchain install
- run: cargo build -p ryo-runtime --release
- run: cargo test
22 changes: 16 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,18 @@ All Ryo code examples **must** use Python-style colons and indentation, **NOT**

## Build & Test Commands

Standard cargo commands work fully out-of-the-box (even on a clean checkout) because `build.rs` automatically compiles the `ryo-runtime` static library in a separate target directory if it isn't found.

```bash
cargo fmt # Auto-format (CI runs --check with -Dwarnings)
cargo clippy --all-targets # Lint (CI enforces, warnings are errors)
cargo check # Check for errors
cargo build [--release] # Build debug or release
cargo build # Automatically builds the runtime (if missing) and then compiles the compiler
cargo check # Check compiler for errors
cargo test # Run all unit + integration tests
cargo run -- run <file> # JIT compile and execute
cargo run -- build <file> # AOT compile to binary
cargo test # Run tests
cargo run -- toolchain install # Download Zig linker
cargo run -- toolchain status # Check Zig status
cargo clippy --all-targets # Lint (warnings are errors)
cargo fmt --check # Check code formatting style
```

**File extensions:** `.ryo` (source), `.md` (docs), `.rs` (Rust), `.o`/`.obj` (generated)
Expand All @@ -65,7 +67,15 @@ GitHub Actions runs on pushes to `main` and PRs targeting `main`: `cargo fmt --c
**Commit prefixes:** `feat:`, `fix:`, `docs:`, `spec:`, `dev:`, `roadmap:`, `test:`, `chore:`, `refactor:`.
Keep subjects under 72 chars. Add body for non-obvious changes.

Never author Claude on commits nor PRs.
IMPORTANT: Never author Claude on commits nor PRs.

---

## Issue Tracking

Non-immediate issues that affect architecture, correctness, or long-term code health go in `ISSUES.md`. Create an entry when you identify a problem that won't be resolved in the current session but must be addressed for better architecture or sustainability. Use the next sequential `I-XXX` number, pick the appropriate severity (Blocking / Correctness / Cleanup), and include Files, Summary, and Resolution fields.

Do **not** create issues for things you're fixing right now — just fix them. Do **not** use GitHub Issues for these; `ISSUES.md` is the single source of truth.

---

Expand Down
86 changes: 86 additions & 0 deletions Cargo.lock

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

15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
[workspace]
members = ["runtime"]

# Without `panic = "abort"`, Rust emits unwinding metadata (`_Unwind_*`
# symbols) that zig cc cannot resolve when linking user binaries.
# These settings live at the workspace root because profile configuration
# isn't permitted in workspace member manifests.
[profile.release]
panic = "abort"

[profile.dev]
panic = "abort"

[package]
name = "ryo"
version = "0.1.0"
edition = "2024"

[build-dependencies]
sha2 = "0.10"

[dependencies]
ryo-runtime = { path = "runtime" }

ariadne = "0.6"
chumsky = "0.12"
Expand Down
5 changes: 5 additions & 0 deletions ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ Resolved entries are removed (not kept around as a changelog). Look at `git log`
**Summary:** Currently, `for-range` loops have bespoke code generation that manually emits basic blocks, jump instructions, and raw counter increments. When general iterators are added, loops should be desugared during the AST-to-UIR phase into standard `while` loops that call `.next()`.
**Resolution:** Once iterators land, remove the `generate_for_range` codegen entirely and rely on standard `while` codegen to emit loops.

### I-043 — Migrate `ryo-runtime` to `#![no_std]`
**Files:** `runtime/src/lib.rs`, `runtime/Cargo.toml`, `src/linker.rs`
**Summary:** The runtime staticlib only uses `std::alloc`, `std::process::abort()`, and `eprintln!`, yet linking against precompiled `std` bundles objects with `_Unwind_*` symbol references. This forces the linker to pass `-lunwind` on Linux (workaround in `src/linker.rs`). Migrating to `#![no_std]` with `extern crate alloc` eliminates the dependency entirely.
**Resolution:** Replace `std::alloc` with `alloc::alloc` (identical API). Replace `eprintln!` + `process::abort()` with `extern "C" { fn abort() -> !; }`. Add `#[panic_handler]` that aborts. Keep the `rlib` crate-type for `cargo test` via a `#[cfg(test)]` std gate. `ryu` already supports `no_std`. Benefits: smaller archive, faster link times, no hidden unwind dependency, simpler cross-compilation.

---

## Cross-References
Expand Down
85 changes: 85 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use sha2::{Digest, Sha256};
use std::env;

fn main() {
Expand All @@ -14,6 +15,90 @@ fn main() {
_ => pkg_version,
};
println!("cargo:rustc-env=RYO_VERSION={version}");

// Runtime archive path. Honor RYO_RUNTIME_LIB if set (used by downstream
// packagers). Otherwise build it on demand using the current cargo profile
// in a separate target directory to avoid cargo lock deadlocks.
let runtime_path = env::var("RYO_RUNTIME_LIB").unwrap_or_else(|_| {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let target_dir =
env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| format!("{manifest_dir}/target"));
let raw_profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
// Mapping rules for Cargo profile resolution:
// - Known "release", "production", and "prod" profiles map to "release".
// - Known "debug" and "dev" profiles map to "debug".
// - For unrecognized/custom profiles, we consult OPT_LEVEL and treat any
// non-"0" optimization level as "release" (since custom optimized profiles
// typically build under optimized target layouts).
// NOTE: Custom profiles with debug = true but OPT_LEVEL > 0 (e.g., opt-level = 1, 2, 3)
// will be classified as "release", avoiding build directory mismatch surprises.
let profile = match raw_profile.as_str() {
"release" | "production" | "prod" => "release",
"debug" | "dev" => "debug",
_ => {
let opt_level = env::var("OPT_LEVEL").unwrap_or_else(|_| "0".to_string());
if opt_level != "0" { "release" } else { "debug" }
}
};
let mut path = std::path::PathBuf::from(&target_dir)
.join(profile)
.join("libryo_runtime.a");
if !path.exists() {
// Build the runtime archive in-process in a separate target directory to avoid deadlocks.
let custom_target_dir =
std::path::PathBuf::from(&manifest_dir).join("target/runtime-build");
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
let mut cmd = std::process::Command::new(&cargo);
cmd.arg("build")
.arg("-p")
.arg("ryo-runtime")
.arg("--target-dir")
.arg(&custom_target_dir);
if profile == "release" {
cmd.arg("--release");
}
let status = cmd
.status()
.unwrap_or_else(|e| panic!("failed to spawn `cargo build -p ryo-runtime`: {e}"));
if status.success() {
path = custom_target_dir.join(profile).join("libryo_runtime.a");
}
}
if !path.exists() {
panic!(
"libryo_runtime.a still missing at {} after build attempt",
path.display()
);
}
// Safely check if path contains non-UTF-8 characters, providing clear instructions if so.
match path.to_str() {
Some(s) => s.to_string(),
None => {
panic!(
"The resolved runtime library path at '{}' contains non-UTF-8 characters. \
Please set the RYO_RUNTIME_LIB environment variable explicitly to override it.",
path.display()
);
}
}
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

println!("cargo:rustc-env=RYO_RUNTIME_LIB={runtime_path}");
println!("cargo:rerun-if-env-changed=RYO_RUNTIME_LIB");
println!("cargo:rerun-if-changed={runtime_path}");

let runtime_bytes = std::fs::read(&runtime_path).unwrap_or_else(|e| {
panic!("failed to read runtime lib at {}: {}", runtime_path, e);
});
let mut hasher = Sha256::new();
hasher.update(&runtime_bytes);
let hash_result = hasher.finalize();
let hash_string = format!("{:x}", hash_result);
println!("cargo:rustc-env=RYO_RUNTIME_HASH={hash_string}");

let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let runtime_src = std::path::PathBuf::from(&manifest_dir).join("runtime/src");
println!("cargo:rerun-if-changed={}", runtime_src.display());
}

fn resolve_git_ref() -> Option<String> {
Expand Down
6 changes: 3 additions & 3 deletions docs/dev/implementation_roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -903,8 +903,8 @@ Every downstream milestone in Phase 2 (structs, tuples, enums, pattern matching,
- **F-strings (`f"Value: {x}"`) are deferred to v0.2** — see Phase 5: F-strings & String Interpolation. v0.1 uses `+` concatenation with the `*_to_str` helpers above.
- Parser/AST: accept the **`move` keyword** as a prefix on parameter declarations (`fn consume(move s: str)`). Without `move`, parameters borrow. Sema records the convention on the function signature (type-only; ownership lives elsewhere).
- Add a **new pipeline stage `src/ownership.rs`** between Sema and Codegen — modeled on Mojo's MLIR-based lifetime/ASAP-destruction passes (Zig stops being a useful compiler reference for the borrow checker; see [mojo_reference.md](mojo_reference.md)). The pass mutates each `Tir` in place: inserts `TirTag::Free`, tracks per-`TirRef` ownership state, and reports diagnostics.
- Per-`TirRef` (SSA value) state lattice: `NotTracked` / `Valid` / `Moved { moved_at, moved_via }`
- `current_owner: HashMap<StringId, TirRef>` shadow table for named bindings
- Per-`TirRef` (SSA value) state lattice: `NotTracked` / `Valid` / `Borrowed` / `Moved { moved_at, kind }`
- `current_owner: HashMap<StringId, TirRef>` shadow table for named bindings (resolves binding-read sites and feeds diagnostics)
- Implicit immutable borrow for function parameters (Rule 2); `move` opts into ownership transfer (Rule 4)
- Standard forward dataflow with CFG-join merges; loop fixed-point (typically converges in 2 iterations)
- Reassignment of `mut` move-typed bindings frees the prior buffer
Expand Down Expand Up @@ -952,7 +952,7 @@ fn main():
- Move tracking covers **named bindings and anonymous owned temporaries** in this milestone. Explicit `&T` / `inout T` borrow syntax arrives in M8.2 / M8.3; field-by-field move tracking (partial moves out of structs/tuples) follows naturally because the same dataflow analysis is reused.
- `str` deallocation follows hybrid eager destruction (spec Section 5.4) — `Free` is inserted after the binding's last use, after the old buffer when a `mut` binding is reassigned over a `Valid` slot, and at the end of the enclosing statement for anonymous owned temporaries. Lexical scope-exit RAII would be too late and would leak intermediate buffers in concat chains. User-extensible cleanup via the `drop` method lands in M23.
- Allocator failure surfaces as a panic in v0.1; allocation-fallible APIs ship alongside error unions (M13).
- Detailed design: see [2026-05-11-milestone-8.1-heap-str-and-move-semantics-design.md](../superpowers/specs/2026-05-11-milestone-8.1-heap-str-and-move-semantics-design.md).
- Detailed design: see [2026-05-20-milestone-8.1-heap-str-and-move-semantics-design.md](../superpowers/specs/2026-05-20-milestone-8.1-heap-str-and-move-semantics-design.md).
- Dependencies: Milestone 8 (control flow blocks shape the dataflow regions the move tracker walks).

### Milestone 8.2: Immutable Borrows (`&T`) [alpha]
Expand Down
11 changes: 11 additions & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "ryo-runtime"
version = "0.1.0"
edition = "2024"
Comment thread
artefactop marked this conversation as resolved.

[lib]
crate-type = ["staticlib", "rlib"]
# rlib is needed for `cargo test` to work (staticlib alone doesn't support test harness)

[dependencies]
ryu = "1"
Loading
Loading