Skip to content

t27o (Ouroboros): Rust front end MVP -- skeleton, normalisation and a rustc oracle, written in t27 (Closes #8966) - #8970

Merged
gHashTag merged 1 commit into
masterfrom
feat/t27o-rust-8966
Oct 11, 2026
Merged

gHashTag merged 1 commit into
masterfrom
feat/t27o-rust-8966

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Closes #8966

t27o (Ouroboros) converts code written in other languages into .t27, and the converter is itself written in t27. This PR adds the Rust front end as one spec: specs/tools/t27o/rust_convert.t27. It has 16 tests, each of which converts a Rust snippet and compares the whole output text. They pass under both t27c test-report and t27b test, and its t27c gen-rust output compiles with rustc. The MVP was run on the lab through a throwaway driver that is not committed.

Pipeline

  1. Skeleton. Every item is read. A struct, enum or const that converts is written.
    • A fn whose body is refused keeps its signature with an empty body (declared, not implemented), plus a comment naming the construct and its line.
    • An item that uses a refused name, an external one (from use or a module path) or one nobody declares is refused with it. Conversion runs in passes until the set of refused names stops growing, so no converted fn or test reaches a stub. Every output compiles.
  2. Normalisation into the subset.
    • &mut self becomes self: *T, and a call becomes T_m(&x).
    • for-each over s, s.iter() or &s; .iter().enumerate() becomes an index loop.
    • if let Some(v) = e becomes e != null and e.?.
    • *r of a shared reference becomes r, and let _x = e becomes a discard.
    • An unused parameter gets _ = p;, and #[cfg] variants are refused whole.
  3. Oracle. oracle() emits a Rust harness made of the original text of everything that converted, plus calls on sample and edge inputs. Those inputs are scalars, &str, &[u8], and every variant when self is a unit enum.
    • rustc runs the harness on the lab. Its output is t27 test oracle_<fn> blocks: the original program's behaviour, which the t27 bodies must reproduce. Inputs on which Rust panics are skipped, because they are outside the contract.
    • Rust #[test] fns are translated too.
  4. Reuse. For each fn with oracle vectors, t27c dupes --name finds same-named t27 functions, and they are run against the vectors.

Measured on the lab

Totals for the 18 trios sources with bee ports (specs/port/trios/**) and for cli/t27b/src/lower.rs:

trios lower.rs
fns seen 124 221
bodies converted 21 0
kept as stubs 6 3
skeleton-clean files (t27c + zig) 18 / 18 yes
skeleton-clean files (t27b) 17 / 18 (1) yes
Rust tests translated / passing 7 of 44 / 7 none
fns with oracle tests / passing 10 / 10 0
behaviourally equivalent fns 15 of 21 bodies 0
satisfied by existing t27 (reuse) 3 (2) 0

(1) trinity_loss: t27b does not yet take the typed array literal [-@as(i8, 1)] ** 64. That is a gap in t27b, not in the converter.
(2) phi_add, phi_mul and phi_subtract match the bee port specs/port/trios/crates/trios-trinity-brain/src/phi.t27 on the oracle vectors. The other 7 same-named candidates are bee ports that do not compile.

Before this pipeline, the first version emitted 47 bodies, but 11 of 19 outputs did not compile. The cascade plus the skeleton is what turns "converted" into "compiles".

Comparison with the bee ports.

  • 4 of the 18 bee ports do not compile: TR-00, GF-01, clade-improve/variant and AH-01.
  • t27o's translated Rust tests pass on the SR-00, SR-01 and SR-02 bee ports.
  • The trinity_loss bee port changed the API, from &[i8] to [64]i8, so the original Rust tests no longer apply to it.

lower.rs targets of #8590-#8592.

  • decl_line, parse_int, int_lit_width, zig_renders and is_prose_clause are already thin wrappers over generated t27 modules (st:: = source_text.t27, aw:: = ast_walk.t27). They are refused as "uses external", and mapping #[path] gen modules to use is step 8 of t27o (Ouroboros): next Rust subset steps, ranked by the refusal table #8968.
  • kind_name and cse_hoist_defect return String, and zig_mutates returns a tuple.
  • So the logic of these candidates already lives in t27, and t27o confirms that only the glue remains.

Top first-refusal reasons.

  • trios: #[cfg] variant 12, String 9, Result 7, Vec 6, impl fmt::Display 6, methods on other crates' types 12, tuple structs 9, cascades (uses refused 17, uses external 9).
  • lower.rs: the R<T> Result alias 96, Vec 33, String 13, tuples 12, HashSet 7.

Effect of each normalisation rule.

  • &mut self removed 140 first refusals (139 in lower.rs). In this sample it converted no extra fn, because the next blocker is a heap or AST type.
  • Enum-variant and &str oracle inputs raised the oracle-checked fns from 3 to 10.

Speed. t27o converts lower.rs (7756 lines) in 0.12 s, and all 18 trios files in 0.01 s.

Gates

Follow-ups

🤖 Generated with Claude Code

… rustc oracle, written in t27 (Closes #8966)

specs/tools/t27o/rust_convert.t27 converts a subset of Rust to t27. It is one spec, 16 tests,
passing under t27c test-report and t27b test, and its gen-rust output compiles with rustc.
- Skeleton: a refused fn keeps its signature with an empty body. Anything that uses a refused,
  external or undeclared name is refused with it, in passes to a fixpoint, so the output compiles.
- Normalisation: &mut self as *T, for-each and enumerate, if let Some, deref of a shared reference,
  let _x, unused parameters, #[cfg] variants.
- Oracle: oracle() writes a Rust harness of what converted, plus sample and edge calls. rustc runs
  it, and its output is t27 `test oracle_<fn>` blocks.

Also: the seal, one t27b ledger row (all 1366 rows pass, max_not_pass stays 0), and one AGENTS.md
bullet. Hand-written lines: 0. own_language check_budget and check_all exit 0. The
duplicate-bodies ratchet gains no group. Follow-ups: see #8967, #8968, #8969.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gHashTag
gHashTag enabled auto-merge (squash) October 11, 2026 08:10
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-11 08:10:15 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 49
PRs with All Checks Green 1
READY 0
FAILING 49
PENDING 0
NO CHECKS YET 0

These columns do not partition: 0 + 49 + 0 + 0 = 49, and there are 50 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=465abf513dc2 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

t27o (Ouroboros): Rust front end MVP -- skeleton, normalisation and a rustc oracle, written in t27

2 participants