use object::{Object, ObjectSection, ObjectSymbol};
use std::process::Command;
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn fixture_wasm() -> Vec<u8> {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("scripts/repro/fact_spec_div_494.wat");
let wat = std::fs::read(path).expect("read fixture wat");
wat::parse_bytes(&wat)
.expect("fixture wat must parse")
.into_owned()
}
fn leb_u32(mut v: u32, out: &mut Vec<u8>) {
loop {
let mut b = (v & 0x7f) as u8;
v >>= 7;
if v != 0 {
b |= 0x80;
}
out.push(b);
if v == 0 {
return;
}
}
}
fn leb_s64(mut v: i64, out: &mut Vec<u8>) {
loop {
let mut b = (v & 0x7f) as u8;
v >>= 7;
let done = (v == 0 && b & 0x40 == 0) || (v == -1 && b & 0x40 != 0);
if !done {
b |= 0x80;
}
out.push(b);
if done {
return;
}
}
}
enum Fact {
Range(u32, u32, i64, i64),
NonZero(u32, u32),
}
fn with_facts(mut wasm: Vec<u8>, facts: &[Fact]) -> Vec<u8> {
let mut payload = vec![0x01]; leb_u32(facts.len() as u32, &mut payload);
for f in facts {
match f {
Fact::Range(func, vid, lo, hi) => {
let mut body = Vec::new();
leb_s64(*lo, &mut body);
leb_s64(*hi, &mut body);
payload.push(0x01);
leb_u32(*func, &mut payload);
leb_u32(*vid, &mut payload);
leb_u32(body.len() as u32, &mut payload);
payload.extend_from_slice(&body);
}
Fact::NonZero(func, vid) => {
payload.push(0x03);
leb_u32(*func, &mut payload);
leb_u32(*vid, &mut payload);
leb_u32(0, &mut payload); }
}
}
let name = b"wsc.facts";
let mut content = Vec::new();
leb_u32(name.len() as u32, &mut content);
content.extend_from_slice(name);
content.extend_from_slice(&payload);
wasm.push(0x00);
leb_u32(content.len() as u32, &mut wasm);
wasm.extend_from_slice(&content);
wasm
}
fn proven_facts() -> Vec<Fact> {
vec![
Fact::Range(0, 1, 1, 64),
Fact::Range(1, 1, 1, 64),
Fact::Range(2, 1, 1, 64),
Fact::NonZero(3, 1),
]
}
fn compile(wasm: &[u8], tag: &str, fact_spec: bool, force_admit: bool) -> Compiled {
let dir = std::env::temp_dir().join("fact_spec_div_494");
std::fs::create_dir_all(&dir).expect("mk tempdir");
let input = dir.join(format!("{tag}.wasm"));
let elf = dir.join(format!("{tag}.elf"));
std::fs::write(&input, wasm).expect("write wasm");
let mut cmd = Command::new(synth());
cmd.args([
"compile",
input.to_str().unwrap(),
"-o",
elf.to_str().unwrap(),
"-b",
"arm",
"--target",
"cortex-m4",
"--all-exports",
]);
if fact_spec {
cmd.env("SYNTH_FACT_SPEC", "1");
} else {
cmd.env_remove("SYNTH_FACT_SPEC");
}
if force_admit {
cmd.env("SYNTH_FACT_SPEC_FORCE_ADMIT", "1");
} else {
cmd.env_remove("SYNTH_FACT_SPEC_FORCE_ADMIT");
}
let out = cmd.output().expect("run synth");
assert!(
out.status.success(),
"compile of '{tag}' failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let text_section = obj.section_by_name(".text").expect(".text");
let text = text_section.data().expect("read .text").to_vec();
let text_addr = text_section.address();
let text_end = text_addr + text.len() as u64;
let mut syms: Vec<(String, u64)> = obj
.symbols()
.filter(|s| {
let addr = s.address() & !1;
!s.name().unwrap_or("").is_empty() && addr >= text_addr && addr < text_end
})
.map(|s| {
(
s.name().unwrap().to_string(),
(s.address() & !1) - text_addr,
)
})
.collect();
syms.sort_by_key(|&(_, a)| a);
let mut funcs = std::collections::HashMap::new();
for (i, (name, start)) in syms.iter().enumerate() {
let end = syms
.get(i + 1)
.map(|&(_, a)| a as usize)
.unwrap_or(text.len())
.min(text.len());
funcs.insert(name.clone(), text[*start as usize..end].to_vec());
}
Compiled {
text,
funcs,
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
}
}
struct Compiled {
text: Vec<u8>,
funcs: std::collections::HashMap<String, Vec<u8>>,
stderr: String,
}
impl Compiled {
fn halfword_count(&self, func: &str, hw: u16) -> usize {
let code = self
.funcs
.get(func)
.unwrap_or_else(|| panic!("symbol '{func}' missing from symtab"));
code.chunks_exact(2)
.filter(|c| u16::from_le_bytes([c[0], c[1]]) == hw)
.count()
}
}
const UDF0: u16 = 0xDE00; const UDF1: u16 = 0xDE01;
#[test]
fn flag_off_with_facts_is_byte_identical_494() {
let base = fixture_wasm();
let facts = with_facts(base.clone(), &proven_facts());
let t_base = compile(&base, "off_base", false, false);
let t_facts = compile(&facts, "off_facts", false, false);
assert_eq!(
t_base.text, t_facts.text,
"SYNTH_FACT_SPEC unset must never consume facts"
);
}
#[test]
fn proven_facts_elide_guards_and_retain_i64_overflow_guard_494() {
let base = fixture_wasm();
let facts = with_facts(base.clone(), &proven_facts());
let b = compile(&base, "proven_base", false, false);
let s = compile(&facts, "proven_spec", true, false);
assert_eq!(b.halfword_count("qu", UDF0), 1, "qu zero guard present");
assert_eq!(b.halfword_count("qs", UDF0), 1, "qs zero guard present");
assert_eq!(b.halfword_count("qs", UDF1), 1, "qs overflow guard present");
assert_eq!(b.halfword_count("ru", UDF0), 1, "ru zero guard present");
assert_eq!(
b.halfword_count("qs64", UDF0),
2,
"qs64: zero trap + overflow trap"
);
assert_eq!(s.halfword_count("qu", UDF0), 0, "qu zero guard elided");
assert_eq!(s.halfword_count("qs", UDF0), 0, "qs zero guard elided");
assert_eq!(s.halfword_count("qs", UDF1), 0, "qs overflow guard elided");
assert_eq!(s.halfword_count("ru", UDF0), 0, "ru zero guard elided");
assert_eq!(
s.halfword_count("qs64", UDF0),
1,
"qs64: zero guard elided, INT64_MIN/-1 overflow guard RETAINED \
(divisor ≠ 0 does not exclude -1 — the two-guard distinction)"
);
let admits = s.stderr.matches("fact-spec: ADMIT").count();
assert_eq!(admits, 5, "stderr:\n{}", s.stderr);
assert!(
s.stderr.contains("divide-by-zero guard elided")
&& s.stderr.contains("UNSAT(P ∧ divisor == 0)")
&& s.stderr.contains("certificate-checked"),
"zero-guard admits name their obligation:\n{}",
s.stderr
);
assert!(
s.stderr.contains("overflow guard elided")
&& s.stderr.contains("dividend == INT32_MIN ∧ divisor == -1"),
"the i32 overflow admit names its SEPARATE obligation:\n{}",
s.stderr
);
assert!(
s.stderr.contains("fact-spec: DECLINE")
&& s.stderr.contains("overflow-guard obligation Sat")
&& s.stderr.contains("RETAINED"),
"the i64 overflow retention is a loud decline:\n{}",
s.stderr
);
}
#[test]
fn bound_including_zero_declines_and_restores_guard_byte_identically_494() {
let base = fixture_wasm();
let facts = with_facts(base.clone(), &[Fact::Range(0, 1, 0, 64)]);
let b = compile(&base, "sat_base", false, false);
let s = compile(&facts, "sat_spec", true, false);
assert_eq!(
s.stderr.matches("fact-spec: ADMIT").count(),
0,
"a Sat obligation must never admit:\n{}",
s.stderr
);
assert!(
s.stderr.contains("zero-guard obligation Sat") && s.stderr.contains("counterexample"),
"declines are loud and carry a model:\n{}",
s.stderr
);
assert_eq!(
b.text, s.text,
"declined ⇒ the general lowering, byte-identical to baseline"
);
}
#[test]
fn force_admit_red_lever_elides_a_sat_guard_and_screams_494() {
let base = fixture_wasm();
let facts = with_facts(base.clone(), &[Fact::Range(0, 1, 0, 64)]);
let forced = compile(&facts, "forced_spec", true, true);
assert_eq!(
forced.halfword_count("qu", UDF0),
0,
"the forced build must drop the guard — that is the point of the red demo"
);
assert!(
forced.stderr.contains("UNSOUND FORCED ADMIT"),
"the forced admit must scream:\n{}",
forced.stderr
);
}