198 lines
6.2 KiB
Rust
198 lines
6.2 KiB
Rust
use std::{
|
|
env,
|
|
ffi::OsStr,
|
|
fs,
|
|
path::{Path, PathBuf},
|
|
process::{Command, Output},
|
|
sync::atomic::{AtomicUsize, Ordering},
|
|
};
|
|
|
|
static NEXT_FIXTURE_ID: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
const MUTABLE_SCALAR_FIXTURE: &str = r#"(module main)
|
|
|
|
(fn flip_flag ((flag bool)) -> bool
|
|
(var current bool flag)
|
|
(set current (if current
|
|
false
|
|
true))
|
|
current)
|
|
|
|
(fn add_wide_local ((base i64)) -> i64
|
|
(var count i64 base)
|
|
(set count (+ count 2i64))
|
|
count)
|
|
|
|
(fn add_ratio_local ((base f64)) -> f64
|
|
(var amount f64 base)
|
|
(set amount (+ amount 0.5))
|
|
amount)
|
|
|
|
(test "mutable bool local true branch"
|
|
(if (flip_flag true)
|
|
false
|
|
true))
|
|
|
|
(test "mutable bool local false branch"
|
|
(flip_flag false))
|
|
|
|
(test "mutable i64 local"
|
|
(= (add_wide_local 40i64) 42i64))
|
|
|
|
(test "mutable f64 local"
|
|
(= (add_ratio_local 41.5) 42.0))
|
|
|
|
(fn main () -> i32
|
|
0)"#;
|
|
|
|
#[test]
|
|
fn mutable_scalar_locals_compile_lower_and_test() {
|
|
let fixture = write_fixture("mutable-scalar-local", MUTABLE_SCALAR_FIXTURE);
|
|
|
|
let compile = run_glagol([fixture.as_os_str()]);
|
|
let llvm_stdout = String::from_utf8_lossy(&compile.stdout);
|
|
let llvm_stderr = String::from_utf8_lossy(&compile.stderr);
|
|
assert!(
|
|
compile.status.success(),
|
|
"compiler rejected mutable scalar local fixture\nstdout:\n{}\nstderr:\n{}",
|
|
llvm_stdout,
|
|
llvm_stderr
|
|
);
|
|
assert!(
|
|
llvm_stdout.contains("define i1 @flip_flag(i1 %flag)")
|
|
&& llvm_stdout.contains("%current.addr = alloca i1")
|
|
&& llvm_stdout.contains("define i64 @add_wide_local(i64 %base)")
|
|
&& llvm_stdout.contains("%count.addr = alloca i64")
|
|
&& llvm_stdout.contains("define double @add_ratio_local(double %base)")
|
|
&& llvm_stdout.contains("%amount.addr = alloca double")
|
|
&& llvm_stdout.contains("store i1")
|
|
&& llvm_stdout.contains("store i64")
|
|
&& llvm_stdout.contains("store double"),
|
|
"LLVM output lost mutable scalar local shape\nstdout:\n{}",
|
|
llvm_stdout
|
|
);
|
|
assert!(
|
|
llvm_stderr.is_empty(),
|
|
"compiler wrote stderr:\n{}",
|
|
llvm_stderr
|
|
);
|
|
|
|
let tests = run_glagol([OsStr::new("--run-tests"), fixture.as_os_str()]);
|
|
assert_success_stdout(
|
|
tests,
|
|
concat!(
|
|
"test \"mutable bool local true branch\" ... ok\n",
|
|
"test \"mutable bool local false branch\" ... ok\n",
|
|
"test \"mutable i64 local\" ... ok\n",
|
|
"test \"mutable f64 local\" ... ok\n",
|
|
"4 test(s) passed\n",
|
|
),
|
|
"mutable scalar local test runner output",
|
|
);
|
|
|
|
let formatted = run_glagol([OsStr::new("--format"), fixture.as_os_str()]);
|
|
assert_success_stdout(
|
|
formatted,
|
|
&format!("{}\n", MUTABLE_SCALAR_FIXTURE),
|
|
"mutable scalar local formatter output",
|
|
);
|
|
|
|
let surface = run_glagol([
|
|
OsStr::new("--inspect-lowering=surface"),
|
|
fixture.as_os_str(),
|
|
]);
|
|
let surface_stdout = String::from_utf8_lossy(&surface.stdout);
|
|
let surface_stderr = String::from_utf8_lossy(&surface.stderr);
|
|
assert!(
|
|
surface.status.success(),
|
|
"surface lowering rejected mutable scalar local fixture\nstdout:\n{}\nstderr:\n{}",
|
|
surface_stdout,
|
|
surface_stderr
|
|
);
|
|
assert!(
|
|
surface_stdout.contains("local var current: bool")
|
|
&& surface_stdout.contains("set current")
|
|
&& surface_stdout.contains("local var count: i64")
|
|
&& surface_stdout.contains("set count")
|
|
&& surface_stdout.contains("local var amount: f64")
|
|
&& surface_stdout.contains("set amount"),
|
|
"surface lowering lost mutable scalar local declarations\nstdout:\n{}",
|
|
surface_stdout
|
|
);
|
|
assert!(
|
|
surface_stderr.is_empty(),
|
|
"surface lowering wrote stderr:\n{}",
|
|
surface_stderr
|
|
);
|
|
|
|
let checked = run_glagol([
|
|
OsStr::new("--inspect-lowering=checked"),
|
|
fixture.as_os_str(),
|
|
]);
|
|
let checked_stdout = String::from_utf8_lossy(&checked.stdout);
|
|
let checked_stderr = String::from_utf8_lossy(&checked.stderr);
|
|
assert!(
|
|
checked.status.success(),
|
|
"checked lowering rejected mutable scalar local fixture\nstdout:\n{}\nstderr:\n{}",
|
|
checked_stdout,
|
|
checked_stderr
|
|
);
|
|
assert!(
|
|
checked_stdout.contains("local var current : unit")
|
|
&& checked_stdout.contains("set current : unit")
|
|
&& checked_stdout.contains("var current : bool")
|
|
&& checked_stdout.contains("local var count : unit")
|
|
&& checked_stdout.contains("set count : unit")
|
|
&& checked_stdout.contains("var count : i64")
|
|
&& checked_stdout.contains("local var amount : unit")
|
|
&& checked_stdout.contains("set amount : unit")
|
|
&& checked_stdout.contains("var amount : f64"),
|
|
"checked lowering lost typed mutable scalar local flow\nstdout:\n{}",
|
|
checked_stdout
|
|
);
|
|
assert!(
|
|
checked_stderr.is_empty(),
|
|
"checked lowering wrote stderr:\n{}",
|
|
checked_stderr
|
|
);
|
|
}
|
|
|
|
fn run_glagol<I, S>(args: I) -> Output
|
|
where
|
|
I: IntoIterator<Item = S>,
|
|
S: AsRef<OsStr>,
|
|
{
|
|
Command::new(env!("CARGO_BIN_EXE_glagol"))
|
|
.args(args)
|
|
.current_dir(Path::new(env!("CARGO_MANIFEST_DIR")))
|
|
.output()
|
|
.expect("run glagol")
|
|
}
|
|
|
|
fn write_fixture(name: &str, source: &str) -> PathBuf {
|
|
let mut path = env::temp_dir();
|
|
path.push(format!(
|
|
"glagol-mutable-scalar-locals-{}-{}-{}.slo",
|
|
name,
|
|
std::process::id(),
|
|
NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed)
|
|
));
|
|
fs::write(&path, source).unwrap_or_else(|err| panic!("write `{}`: {}", path.display(), err));
|
|
path
|
|
}
|
|
|
|
fn assert_success_stdout(output: Output, expected: &str, context: &str) {
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
assert!(
|
|
output.status.success(),
|
|
"{} failed\nstdout:\n{}\nstderr:\n{}",
|
|
context,
|
|
stdout,
|
|
stderr
|
|
);
|
|
assert_eq!(stdout, expected, "{} stdout drifted", context);
|
|
assert!(stderr.is_empty(), "{} wrote stderr:\n{}", context, stderr);
|
|
}
|