use std::path::PathBuf;
use std::process::Command;
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn wat_file(name: &str, wat: &str) -> PathBuf {
let dir = std::env::temp_dir().join("synth_start_1046_tests");
std::fs::create_dir_all(&dir).expect("mkdir");
let p = dir.join(name);
std::fs::write(&p, wat).expect("write wat");
p
}
fn compile(input: &std::path::Path, extra: &[&str]) -> std::process::Output {
let out = std::env::temp_dir()
.join("synth_start_1046_tests")
.join(format!(
"{}_{}.o",
input.file_stem().unwrap().to_str().unwrap(),
extra.join("").replace(['-', '/', ' '], "")
));
let mut args = vec![
"compile",
input.to_str().unwrap(),
"--all-exports",
"-o",
out.to_str().unwrap(),
];
args.extend_from_slice(extra);
Command::new(synth())
.args(&args)
.output()
.expect("run synth")
}
fn stderr(out: &std::process::Output) -> String {
String::from_utf8_lossy(&out.stderr).into_owned()
}
fn assert_refused(out: &std::process::Output, must_mention: &[&str], ctx: &str) {
assert!(
!out.status.success(),
"{ctx}: expected a loud refusal, got success (exit 0 is the #1046 \
silent-drop bug — the (start ...) section was discarded and the \
module compiled as if its instantiation-time init did not exist).\n\
stderr: {}",
stderr(out)
);
let err = stderr(out);
for needle in must_mention {
assert!(
err.contains(needle),
"{ctx}: refusal does not mention '{needle}'.\nstderr: {err}"
);
}
}
const START_WAT: &str = r#"(module
(memory 1)
(func $init (i32.const 0) (i32.const 42) i32.store)
(start $init)
(func (export "get") (result i32) i32.const 0 i32.load))"#;
#[test]
fn start_on_arm_relocatable_refuses_loudly() {
let f = wat_file("start_arm_reloc.wat", START_WAT);
let out = compile(&f, &["--relocatable", "--target", "cortex-m4"]);
assert_refused(
&out,
&["start function", "#1046"],
"(start) on ARM --relocatable",
);
}
#[test]
fn start_on_arm_selfcontained_refuses_loudly() {
let f = wat_file("start_arm_sc.wat", START_WAT);
let out = compile(&f, &["--target", "cortex-m4"]);
assert_refused(
&out,
&["start function", "#1046"],
"(start) on ARM self-contained",
);
}
#[test]
fn start_on_a32_relocatable_refuses_loudly() {
let f = wat_file("start_a32.wat", START_WAT);
let out = compile(&f, &["--relocatable", "--target", "cortex-r5"]);
assert_refused(
&out,
&["start function", "#1046"],
"(start) on cortex-r5 --relocatable",
);
}
#[test]
fn start_on_riscv_refuses_loudly() {
let f = wat_file("start_rv32.wat", START_WAT);
let out = compile(&f, &["-b", "riscv", "--target", "rv32imac"]);
assert_refused(
&out,
&["start function", "#1046"],
"(start) on RISC-V rv32imac",
);
}
#[test]
fn start_on_aarch64_refuses_loudly() {
let f = wat_file("start_a64.wat", START_WAT);
let out = compile(&f, &["-b", "aarch64"]);
assert_refused(&out, &["start function", "#1046"], "(start) on aarch64");
}
#[test]
fn exported_start_function_still_refuses() {
let f = wat_file(
"start_exported.wat",
r#"(module
(memory 1)
(func $init (export "init") (i32.const 0) (i32.const 42) i32.store)
(start $init)
(func (export "get") (result i32) i32.const 0 i32.load))"#,
);
let out = compile(&f, &["--relocatable", "--target", "cortex-m4"]);
assert_refused(
&out,
&["start function", "#1046"],
"(start) naming an exported function",
);
}
#[test]
fn start_on_single_function_path_refuses_loudly() {
let f = wat_file("start_single.wat", START_WAT);
let out_path = std::env::temp_dir()
.join("synth_start_1046_tests")
.join("start_single_fn.o");
let out = Command::new(synth())
.args([
"compile",
f.to_str().unwrap(),
"--func-name",
"get",
"--relocatable",
"--target",
"cortex-m4",
"-o",
out_path.to_str().unwrap(),
])
.output()
.expect("run synth");
assert_refused(
&out,
&["start function", "#1046"],
"(start) on the single-function path",
);
}
#[test]
fn start_free_module_still_compiles_everywhere() {
let f = wat_file(
"no_start.wat",
r#"(module
(memory 1)
(func $init (i32.const 0) (i32.const 42) i32.store)
(func (export "get") (result i32) i32.const 0 i32.load))"#,
);
for (ctx, extra) in [
(
"ARM --relocatable",
&["--relocatable", "--target", "cortex-m4"][..],
),
("ARM self-contained", &["--target", "cortex-m4"][..]),
("RISC-V", &["-b", "riscv", "--target", "rv32imac"][..]),
("aarch64", &["-b", "aarch64"][..]),
] {
let out = compile(&f, extra);
assert!(
out.status.success(),
"{ctx}: start-free control module must compile (exit 0), got \
failure.\nstderr: {}",
stderr(&out)
);
}
}