The Rust backend already knows how to translate every math builtin, and never gets the chance to.
The defect
zig_builtin_to_rust maps the whole family correctly — "@min" if two => format!("({}).min({})", ...), "@sqrt" | "@abs" | "@round" | ... => format!("({}).{}()", ...). Its first line is:
fn zig_builtin_to_rust(name: &str, args: &[String]) -> Option<String> {
if !name.starts_with(char::from(64u8)) { // no `@` sigil
return None;
}
Specs write the builtins bare — abs(x), min(a, b) — so the table never answers, and the bare name is emitted verbatim. Rust has none of these as free functions, and rustc replies:
error[E0425]: cannot find function `abs` in this scope
The Zig backend closed this identical class at gen_expr, with a comment naming the same symptom ("use of undeclared identifier abs") and a declared_fns guard. The Rust backend has neither.
Measured
- 118 of 650 specs contain a call-shaped occurrence of one of the seven; 99 of those parse.
- Corpus-wide rustc acceptance of the emitted Rust is 429 of 650 (instrument:
rustc --edition 2021 --crate-type lib --crate-name g --emit=metadata, with a control that compiles a valid file and rejects an invalid one — an earlier run of this measurement read 0 of 650 because the filenames carried .t27 and rustc derives the crate name from the filename, so nothing was ever compiled).
Three traps in the obvious repair
Each was measured, not anticipated:
-
A spec may own the name. 30 declarations across the corpus give one of these names to a spec function — fn floor in 10 specs, fn abs in 9, fn cos in 3. Redirecting those to a Rust method is a wrong translation that compiles, which is strictly worse than a bare name that does not. 3 of the 30 declare their return type Zig-style without ->, so a guard built on collect_fn_ret_types (which skips an empty extra_return_type) would be narrower than its subject exactly where it matters.
-
A literal receiver has no type. (5.0).sqrt() is error[E0689]: can not call method sqrt on ambiguous numeric type {float}, and so is ((2.0 / 3.141592653589793)).sqrt() — the first version of the guard tested for a single literal token and still admitted the compound form.
-
A const initialiser can never work. sqrt is not a const fn; inside pub const K: f64 = ... every spelling fails. Trading E0425 for E0015 there is a new failure, not a repair.
Separately: @log is mapped wrongly
The existing table has "@log" ... => format!("({}).{}()", ...), emitting (x).log(). Rust’s f64::log takes a base argument, so that is error[E0061]: this method takes 1 argument but 0 arguments were supplied. The natural log is ln. Filed here rather than changed under an unrelated title.
What the repair is worth
Honestly: corpus-wide rustc acceptance moves 429 → 429, a delta of zero. The emitted Rust of 9 files changes and none crosses from fail to pass — each carries other blockers, matching the repository’s established shape that a rejected file has four or five error families, not one.
The value is not the corpus number. It is that a clean spec can now produce Rust that compiles at all, which is the precondition for expressing hand-written code as specs. A worked example is in the PR: a spec port of rings/ring-103-rust/src/lib.rs whose generated Rust agrees with the hand-written original bit-exactly on 20,736 inputs, including NaN, both infinities, both zeros and subnormals.
The Rust backend already knows how to translate every math builtin, and never gets the chance to.
The defect
zig_builtin_to_rustmaps the whole family correctly —"@min" if two => format!("({}).min({})", ...),"@sqrt" | "@abs" | "@round" | ... => format!("({}).{}()", ...). Its first line is:Specs write the builtins bare —
abs(x),min(a, b)— so the table never answers, and the bare name is emitted verbatim. Rust has none of these as free functions, and rustc replies:The Zig backend closed this identical class at
gen_expr, with a comment naming the same symptom ("use of undeclared identifierabs") and adeclared_fnsguard. The Rust backend has neither.Measured
rustc --edition 2021 --crate-type lib --crate-name g --emit=metadata, with a control that compiles a valid file and rejects an invalid one — an earlier run of this measurement read 0 of 650 because the filenames carried.t27and rustc derives the crate name from the filename, so nothing was ever compiled).Three traps in the obvious repair
Each was measured, not anticipated:
A spec may own the name. 30 declarations across the corpus give one of these names to a spec function —
fn floorin 10 specs,fn absin 9,fn cosin 3. Redirecting those to a Rust method is a wrong translation that compiles, which is strictly worse than a bare name that does not. 3 of the 30 declare their return type Zig-style without->, so a guard built oncollect_fn_ret_types(which skips an emptyextra_return_type) would be narrower than its subject exactly where it matters.A literal receiver has no type.
(5.0).sqrt()iserror[E0689]: can not call method sqrt on ambiguous numeric type {float}, and so is((2.0 / 3.141592653589793)).sqrt()— the first version of the guard tested for a single literal token and still admitted the compound form.A const initialiser can never work.
sqrtis not aconst fn; insidepub const K: f64 = ...every spelling fails. Trading E0425 for E0015 there is a new failure, not a repair.Separately:
@logis mapped wronglyThe existing table has
"@log" ... => format!("({}).{}()", ...), emitting(x).log(). Rust’sf64::logtakes a base argument, so that iserror[E0061]: this method takes 1 argument but 0 arguments were supplied. The natural log isln. Filed here rather than changed under an unrelated title.What the repair is worth
Honestly: corpus-wide rustc acceptance moves 429 → 429, a delta of zero. The emitted Rust of 9 files changes and none crosses from fail to pass — each carries other blockers, matching the repository’s established shape that a rejected file has four or five error families, not one.
The value is not the corpus number. It is that a clean spec can now produce Rust that compiles at all, which is the precondition for expressing hand-written code as specs. A worked example is in the PR: a spec port of
rings/ring-103-rust/src/lib.rswhose generated Rust agrees with the hand-written original bit-exactly on 20,736 inputs, including NaN, both infinities, both zeros and subnormals.