diff --git a/.github/scripts/run_own_tests.sh b/.github/scripts/run_own_tests.sh new file mode 100755 index 0000000..bed6df7 --- /dev/null +++ b/.github/scripts/run_own_tests.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Execute our own set of tests using a local `compiletest` tool based on `ui_test`. +set -e +set -u + +# Where we will store the SMIR tools (Optional). +TOOLS_BIN="${TOOLS_BIN:-"/tmp/smir/bin"}" +# Assume we are inside SMIR repository +SMIR_PATH=$(git rev-parse --show-toplevel) +export RUST_BACKTRACE=1 + +# Build stable_mir tools +function build_smir_tools() { + pushd "${SMIR_PATH}" + cargo +nightly build -Z unstable-options --out-dir "${TOOLS_BIN}" + export PATH="${TOOLS_BIN}":"${PATH}" +} + +# Run tests +function run_tests() { + SUITES=( + "sanity-checks pass" + "fixme fix-me" + ) + for suite_cfg in "${SUITES[@]}"; do + # Hack to work on older bash like the ones on MacOS. + suite_pair=($suite_cfg) + suite=${suite_pair[0]} + mode=${suite_pair[1]} + echo "#### Running suite: ${suite} mode: ${mode}" + compiletest \ + --driver-path="${TOOLS_BIN}/test-drive" \ + --mode=${mode} \ + --src-base="tests/${suite}" \ + --output-dir="target/tests/" \ + --no-capture + done +} + +build_smir_tools +run_tests diff --git a/.github/scripts/run_rustc_tests.sh b/.github/scripts/run_rustc_tests.sh new file mode 100755 index 0000000..455d1d8 --- /dev/null +++ b/.github/scripts/run_rustc_tests.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +# Run rustc test suites using our test driver using nightly. +# This script leverages the rustc's repo compiletest crate. +# +# The suites configuration should match: +# https://github.com/rust-lang/rust/blob/master/src/bootstrap/test.rs + +set -e +set -u +export RUST_BACKTRACE=1 + +# Location of a rust repository. Clone one if path doesn't exist. +RUST_REPO="${RUST_REPO:-"/tmp/rustc"}" + +# Where we will store the SMIR tools (Optional). +TOOLS_BIN="${TOOLS_BIN:-"/tmp/smir/bin"}" + +# Assume we are inside SMIR repository +SMIR_PATH=$(git rev-parse --show-toplevel) + +# Build stable_mir tools +function build_smir_tools() { + pushd "${SMIR_PATH}" + cargo +nightly build -Z unstable-options --out-dir "${TOOLS_BIN}" + export PATH="${TOOLS_BIN}":"${PATH}" +} + +# Set up rustc repository +function setup_rustc_repo() { + if [[ ! -e "${RUST_REPO}" ]]; then + mkdir -p "$(dirname ${RUST_REPO})" + git clone -b master https://github.com/rust-lang/rust.git "${RUST_REPO}" + pushd "${RUST_REPO}" + commit="$(rustc +nightly -vV | awk '/^commit-hash/ { print $2 }')" + git checkout ${commit} + git submodule init -- "${RUST_REPO}/library/stdarch" + git submodule update + else + pushd "${RUST_REPO}" + fi +} + +function run_tests() { + # Run the following suite configuration for now (test suite + mode) + SUITES=( + "codegen codegen" + "codegen-units codegen-units" + # -- The suites below are failing because of fully qualified paths for standard library + # E.g.: + # - _10 = _eprint(move _11) -> [return: bb6, unwind unreachable]; + # + _10 = std::io::_eprint(move _11) -> [return: bb6, unwind unreachable]; + # + #"ui ui" + #"mir-opt mir-opt" + #"pretty pretty" -- 2 failing tests + ) + + SYSROOT=$(rustc +nightly --print sysroot) + PY_PATH=$(type -P python3) + HOST=$(rustc +nightly -vV | awk '/^host/ { print $2 }') + FILE_CHECK="$(which FileCheck-12 || which FileCheck-13 || which FileCheck-14)" + + for suite_cfg in "${SUITES[@]}"; do + # Hack to work on older bash like the ones on MacOS. + suite_pair=($suite_cfg) + suite=${suite_pair[0]} + mode=${suite_pair[1]} + + echo "#### Running suite: ${suite} mode: ${mode}" + cargo +nightly run -p compiletest -- \ + --compile-lib-path="${SYSROOT}/lib" \ + --run-lib-path="${SYSROOT}/lib"\ + --python="${PY_PATH}" \ + --rustc-path="${TOOLS_BIN}/test-drive" \ + --mode=${mode} \ + --suite="${suite}" \ + --src-base="tests/${suite}" \ + --build-base="$(pwd)/build/${HOST}/stage1/tests/${suite}" \ + --sysroot-base="$SYSROOT" \ + --stage-id=stage1-${HOST} \ + --cc= \ + --cxx= \ + --cflags= \ + --cxxflags= \ + --llvm-components= \ + --android-cross-path= \ + --target=${HOST} \ + --llvm-filecheck="${FILE_CHECK}" \ + --channel=nightly \ + --target-rustcflags="--smir-check" \ + --host-rustcflags="--smir-check" + done +} + +build_smir_tools +setup_rustc_repo +run_tests diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000..8f7bf0a --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,33 @@ +name: Run compiler tests + +on: + schedule: + - cron: "0 6 * * *" # Run daily at 06:00 UTC + workflow_dispatch: # Allow manual dispatching + pull_request: + +jobs: + compile-test: + name: Rust Compiler Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + + - name: Test local suites + run: ./.github/scripts/run_own_tests.sh + env: + TOOLS_BIN: "/tmp/smir/bin" + + - name: Test rustc suites + run: ./.github/scripts/run_rustc_tests.sh + env: + RUST_REPO: "/tmp/rustc" + TOOLS_BIN: "/tmp/smir/bin" + # Don't fail CI for now. See: https://github.com/rust-lang/project-stable-mir/issues/39 + continue-on-error: true diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e90ec9c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +# Cargo workspace for utility tools used to check stable-mir in CI +[workspace] +resolver = "2" +members = [ + "tools/compiletest", + "tools/test-drive", +] + +exclude = [ + "build", + "target", +] diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..ee9dc17 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly" +components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/fixme/associated-items/methods.rs b/tests/fixme/associated-items/methods.rs new file mode 100644 index 0000000..4d1ac0d --- /dev/null +++ b/tests/fixme/associated-items/methods.rs @@ -0,0 +1,83 @@ +//! Example derived from +#![feature(box_into_inner)] + +use std::pin::Pin; +use std::rc::Rc; +use std::sync::Arc; + +#[derive(Default, Debug)] +struct Example { + inner: String, +} + +type Alias = Example; +trait Trait { + type Output; +} +impl Trait for Example { + type Output = Example; +} + +#[allow(unused)] +impl Example { + pub fn by_value(self: Self) { + self.by_ref("by_val"); + } + + pub fn by_ref(self: &Self, source: &str) { + println!("{source}: {}", self.inner); + } + + pub fn by_ref_mut(self: &mut Self) { + self.inner = "by_ref_mut".to_string(); + self.by_ref("mut"); + } + + pub fn by_box(self: Box) { + self.by_ref("by_box"); + Box::into_inner(self).by_value(); + } + + pub fn by_rc(self: Rc) { + self.by_ref("by_rc"); + } + + pub fn by_arc(self: Arc) { + self.by_ref("by_arc"); + } + + pub fn by_pin(self: Pin<&Self>) { + self.by_ref("by_pin"); + } + + pub fn explicit_type(self: Arc) { + self.by_ref("explicit"); + } + + pub fn with_lifetime<'a>(self: &'a Self) { + self.by_ref("lifetime"); + } + + pub fn nested<'a>(self: &mut &'a Arc>>) { + self.by_ref("nested"); + } + + pub fn via_projection(self: ::Output) { + self.by_ref("via_projection"); + } + + pub fn from(name: String) -> Self { + Example { inner: name } + } +} + +fn main() { + let example = Example::from("Hello".to_string()); + example.by_value(); + + let boxed = Box::new(Example::default()); + boxed.by_box(); + + Example::default().by_ref_mut(); + Example::default().with_lifetime(); +} diff --git a/tests/sanity-checks/simple/simple_lib.rs b/tests/sanity-checks/simple/simple_lib.rs new file mode 100644 index 0000000..b86f9fe --- /dev/null +++ b/tests/sanity-checks/simple/simple_lib.rs @@ -0,0 +1,23 @@ +//! Just a simple library + +pub struct Point { + pub x: i64, + pub y: i64, +} + +impl Point { + pub fn distance(&self, other: &Point) -> u64 { + let (x_dist, x_over) = (self.x - other.x).overflowing_pow(2); + let (y_dist, y_over) = (self.y - other.y).overflowing_pow(2); + if y_over | x_over { + panic!("overflow"); + } + + let dist = (x_dist as u64 + y_dist as u64) >> 1; + dist + } + + pub fn distance_root(&self) -> u64 { + self.distance(&Point { x: 0, y: 0 }) + } +} diff --git a/tools/compiletest/Cargo.toml b/tools/compiletest/Cargo.toml new file mode 100644 index 0000000..dedf166 --- /dev/null +++ b/tools/compiletest/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "compiletest" +description = "Run tests using compiletest-rs" +version = "0.0.0" +edition = "2021" + +[dependencies] +ui_test = "0.20.0" +clap = { version = "4.1.3", features = ["derive"] } + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)]. +# See https://github.com/rust-analyzer/rust-analyzer/pull/7891 +rustc_private = true diff --git a/tools/compiletest/build.rs b/tools/compiletest/build.rs new file mode 100644 index 0000000..63fcfb5 --- /dev/null +++ b/tools/compiletest/build.rs @@ -0,0 +1,16 @@ +use std::env; +use std::path::PathBuf; + +pub fn main() { + // Add rustup to the rpath in order to properly link with the correct rustc version. + let rustup_home = env::var("RUSTUP_HOME").unwrap(); + let toolchain = env::var("RUSTUP_TOOLCHAIN").unwrap(); + let rustc_lib: PathBuf = [&rustup_home, "toolchains", &toolchain, "lib"] + .iter() + .collect(); + println!( + "cargo:rustc-link-arg-bin=compiletest=-Wl,-rpath,{}", + rustc_lib.display() + ); + println!("cargo:rustc-env=RUSTC_LIB_PATH={}", rustc_lib.display()); +} diff --git a/tools/compiletest/src/args.rs b/tools/compiletest/src/args.rs new file mode 100644 index 0000000..29a044c --- /dev/null +++ b/tools/compiletest/src/args.rs @@ -0,0 +1,121 @@ +use std::ffi::OsString; +/// Create our own parser and build the Config from it. +use std::fmt::Debug; +use std::path::PathBuf; +use ui_test::{CommandBuilder, Config, OutputConflictHandling, RustfixMode}; + +#[derive(Copy, Clone, Debug, clap::ValueEnum)] +/// Decides what is expected of each test's exit status. +pub enum Mode { + /// The test passes a full execution of the rustc driver. + Pass, + /// The test produces an executable binary that can get executed on the host. + Run, + /// The rustc driver should emit an error. + Fail, + /// The test is currently failing but is expected to succeed. + /// This is used to add test cases that reproduce an existing bug. This help us identify issues + /// that may be "accidentally" fixed. + FixMe, + /// Run the tests, but always pass them as long as all annotations are satisfied and stderr files match. + Yolo, + /// The test passes a full execution of `cargo build` + CargoPass, +} + +#[derive(Debug, clap::Parser)] +#[command(version, name = "compiletest")] +pub struct Args { + /// The path where all tests are + #[arg(long)] + src_base: PathBuf, + + /// The mode according to ui_test modes. + #[arg(long, default_value = "yolo")] + mode: Mode, + + /// Path for the stable-mir driver. + #[arg(long)] + driver_path: PathBuf, + + /// Path for where the output should be stored. + #[arg(long)] + output_dir: PathBuf, + + #[arg(long)] + pub verbose: bool, + + /// Run test-driver on verbose mode to print test outputs. + #[arg(long)] + pub no_capture: bool, +} + +impl From for ui_test::Mode { + /// Use rustc configuration as default but override arguments to fit our use case. + fn from(mode: Mode) -> ui_test::Mode { + match mode { + Mode::Pass | Mode::CargoPass => ui_test::Mode::Pass, + Mode::Run => ui_test::Mode::Run { exit_code: 0 }, + Mode::FixMe | + Mode::Fail => ui_test::Mode::Fail { + require_patterns: false, + rustfix: RustfixMode::Disabled, + }, + Mode::Yolo => ui_test::Mode::Yolo { + rustfix: RustfixMode::Disabled, + }, + } + } +} + +impl From for Config { + /// Use rustc configuration as default but override arguments to fit our use case. + fn from(args: Args) -> Config { + let mut config = if matches!(args.mode, Mode::CargoPass) { + cargo_config(&args) + } else { + driver_config(&args) + }; + config.filter(r"\[T-DRIVE\].*\n", ""); + config.mode = ui_test::Mode::from(args.mode); + config.output_conflict_handling = OutputConflictHandling::Error("Should Fail".to_string()); + config.out_dir = args.output_dir; + //config.run_lib_path = PathBuf::from(env!("RUSTC_LIB_PATH")); + config + } +} + +fn rustc_flags(args: &Args) -> Vec { + let mut flags = vec!["--smir-check".into()]; + if args.verbose || args.no_capture { + flags.push("--smir-verbose".into()); + } + if matches!(args.mode, Mode::FixMe) { + // Enable checks that should pass but may trigger an existing issue. + flags.push("--smir-fixme".into()); + } + flags +} + +/// Configure cargo tests that will run the test-driver instead of rustc. +fn cargo_config(args: &Args) -> Config { + let mut config = Config::cargo(args.src_base.clone()); + config.program.envs.push(( + "RUST".into(), + Some(args.driver_path.clone().into_os_string()), + )); + config.program.envs.push(( + "CARGO_ENCODED_RUSTFLAGS".into(), + Some(rustc_flags(args).join(&OsString::from("\x1f")).into()), + )); + config +} + +/// Configure tests that will invoke the test-driver directly as rustc. +fn driver_config(args: &Args) -> Config { + let mut config = Config::rustc(args.src_base.clone()); + config.program = CommandBuilder::rustc(); + config.program.program = args.driver_path.clone(); + config.program.args = rustc_flags(args); + config +} diff --git a/tools/compiletest/src/main.rs b/tools/compiletest/src/main.rs new file mode 100644 index 0000000..1e96c79 --- /dev/null +++ b/tools/compiletest/src/main.rs @@ -0,0 +1,39 @@ +//! Run compiletest on a given folder. + +mod args; + +use clap::Parser; +use std::process::ExitCode; +use ui_test::{status_emitter, Config}; + +fn main() -> ExitCode { + let args = args::Args::parse(); + let verbose = args.verbose; + if verbose { + println!("args: ${args:?}"); + } + let config = Config::from(args); + if verbose { + println!("Compiler: {}", config.program.display()); + } + + let name = config.root_dir.display().to_string(); + + let text = if verbose { + status_emitter::Text::verbose() + } else { + status_emitter::Text::quiet() + }; + + let result = ui_test::run_tests_generic( + vec![config], + ui_test::default_file_filter, + ui_test::default_per_file_config, + (text, status_emitter::Gha:: { name }), + ); + if result.is_ok() { + ExitCode::SUCCESS + } else { + ExitCode::FAILURE + } +} diff --git a/tools/test-drive/Cargo.toml b/tools/test-drive/Cargo.toml new file mode 100644 index 0000000..12b88aa --- /dev/null +++ b/tools/test-drive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "test-drive" +description = "A rustc wrapper that can be used to test stable-mir on a crate" +version = "0.0.0" +edition = "2021" + +[dependencies] + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)]. +# See https://github.com/rust-analyzer/rust-analyzer/pull/7891 +rustc_private = true diff --git a/tools/test-drive/build.rs b/tools/test-drive/build.rs new file mode 100644 index 0000000..44c9940 --- /dev/null +++ b/tools/test-drive/build.rs @@ -0,0 +1,15 @@ +use std::env; +use std::path::PathBuf; + +pub fn main() { + // Add rustup to the rpath in order to properly link with the correct rustc version. + let rustup_home = env::var("RUSTUP_HOME").unwrap(); + let toolchain = env::var("RUSTUP_TOOLCHAIN").unwrap(); + let rustc_lib: PathBuf = [&rustup_home, "toolchains", &toolchain, "lib"] + .iter() + .collect(); + println!( + "cargo:rustc-link-arg-bin=test-drive=-Wl,-rpath,{}", + rustc_lib.display() + ); +} diff --git a/tools/test-drive/src/main.rs b/tools/test-drive/src/main.rs new file mode 100644 index 0000000..8aaf936 --- /dev/null +++ b/tools/test-drive/src/main.rs @@ -0,0 +1,110 @@ +//! Test that users are able to inspec the MIR body of functions and types + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(result_option_inspect)] + +mod sanity_checks; + +extern crate rustc_middle; +extern crate rustc_smir; + +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use rustc_smir::stable_mir::CompilerError; +use std::ops::ControlFlow; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::process::ExitCode; +use std::sync::atomic::{AtomicBool, Ordering}; + +// ---- Arguments that should be parsed by the test-driver (w/ "smir" prefix) +const CHECK_ARG: &str = "--smir-check"; +/// Enable verbose mode. +const VERBOSE_ARG: &str = "--smir-verbose"; +/// Argument used to enable checks that may be failing due to an existing issue. +const FIXME_ARG: &str = "--smir-fixme"; + +// Use a static variable for simplicity. +static VERBOSE: AtomicBool = AtomicBool::new(false); +static FIXME_CHECKS: AtomicBool = AtomicBool::new(false); + +type TestResult = Result<(), String>; + +/// This is a wrapper that can be used to replace rustc. +/// +/// Besides all supported rustc arguments, use `--check-smir` to run all the stable-mir checks. +/// This allows us to use this tool in cargo projects to analyze the target crate only by running +/// `cargo rustc --check-smir`. +fn main() -> ExitCode { + let args = std::env::args(); + let (smir_args, rustc_args) : (Vec, _) = args.partition + (|arg| arg + .starts_with + ("--smir")); + let callback = if smir_args.contains(&CHECK_ARG.to_string()) { + VERBOSE.store(smir_args.contains(&VERBOSE_ARG.to_string()), Ordering::Relaxed); + FIXME_CHECKS.store(smir_args.contains(&FIXME_ARG.to_string()), Ordering::Relaxed); + test_stable_mir + } else { + |_: TyCtxt| ControlFlow::<()>::Continue(()) + }; + let result = rustc_internal::StableMir::new(rustc_args, callback).run(); + if result.is_ok() || matches!(result, Err(CompilerError::Skipped)) { + ExitCode::SUCCESS + } else { + ExitCode::FAILURE + } +} + +macro_rules! run_tests { + ($( $test:path ),+) => { + [$({ + run_test(stringify!($test), || { $test() }) + },)+] + }; +} + +fn info(msg: String) { + if VERBOSE.load(Ordering::Relaxed) { + // We filter output based on [T-DRIVE] prefix. + eprintln!("[T-DRIVE] {}", msg); + } +} + +/// This function invoke other tests and process their results. +/// Tests should avoid panic, +fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> { + let mut results = Vec::from(run_tests![ + sanity_checks::test_entry_fn, + sanity_checks::test_all_fns, + sanity_checks::test_crates + ]); + if FIXME_CHECKS.load(Ordering::Relaxed) { + results.extend_from_slice(&run_tests!(sanity_checks::test_traits)) + } + let (success, failure): (Vec<_>, Vec<_>) = results.iter().partition(|r| r.is_ok()); + info(format!( + "Ran {} tests. {} succeeded. {} failed", + results.len(), + success.len(), + failure.len() + )); + if failure.is_empty() { + ControlFlow::<()>::Continue(()) + } else { + ControlFlow::<()>::Break(()) + } +} + +fn run_test TestResult>(name: &str, f: F) -> TestResult { + let result = match catch_unwind(AssertUnwindSafe(f)) { + Err(_) => Err("Panic: {}".to_string()), + Ok(result) => result, + }; + info(format!( + "Test {}: {}", + name, + result.as_ref().err().unwrap_or(&"Ok".to_string()) + )); + result +} diff --git a/tools/test-drive/src/sanity_checks.rs b/tools/test-drive/src/sanity_checks.rs new file mode 100644 index 0000000..610613d --- /dev/null +++ b/tools/test-drive/src/sanity_checks.rs @@ -0,0 +1,116 @@ +//! Module that contains sanity checks that Stable MIR APIs don't crash and that +//! their result is coherent. +//! +//! These checks should only depend on StableMIR APIs. See other modules for tests that compare +//! the result between StableMIR and internal APIs. +use crate::TestResult; +use rustc_smir::stable_mir; +use std::fmt::Debug; +use std::hint::black_box; + +fn check_equal(val: T, expected: T, msg: &str) -> TestResult +where + T: Debug + PartialEq, +{ + if val != expected { + Err(format!( + "{}: \n Expected: {:?}\n Found: {:?}", + msg, expected, val + )) + } else { + Ok(()) + } +} + +pub fn check(val: bool, msg: String) -> TestResult { + if !val { + Err(msg) + } else { + Ok(()) + } +} + +// Test that if there is an entry point, the function is part of `all_local_items`. +pub fn test_entry_fn() -> TestResult { + let entry_fn = stable_mir::entry_fn(); + entry_fn.map_or(Ok(()), |entry_fn| { + check_body(entry_fn.body()); + let all_items = stable_mir::all_local_items(); + check( + all_items.contains(&entry_fn), + format!("Failed to find entry_fn `{:?}`", entry_fn), + ) + }) +} + +/// Iterate over local function bodies. +pub fn test_all_fns() -> TestResult { + let all_items = stable_mir::all_local_items(); + for item in all_items { + // Get body and iterate over items + let body = item.body(); + check_body(body); + } + Ok(()) +} + +/// Using these structures will always follow calls to get more details about those structures. +/// Unless user is trying to find a specific type, this will get repetitive. +pub fn test_traits() -> TestResult { + // FIXME: All trait declarations only return local traits. + // See https://github.com/rust-lang/project-stable-mir/issues/37 + let all_traits = stable_mir::all_trait_decls(); + for trait_decl in all_traits.iter().map(stable_mir::trait_decl) { + // Can't compare trait_decl, so just compare a field for now. + check_equal( + stable_mir::trait_decl(&trait_decl.def_id).specialization_kind, + trait_decl.specialization_kind, + "external crate mismatch", + )?; + } + + for trait_impl in stable_mir::all_trait_impls() + .iter() + .map(stable_mir::trait_impl) + { + check( + all_traits.contains(&trait_impl.value.def_id), + format!("Failed to find trait definition {trait_impl:?}"), + )?; + } + Ok(()) +} + +pub fn test_crates() -> TestResult { + for krate in stable_mir::external_crates() { + check_equal( + stable_mir::find_crate(&krate.name.as_str()), + Some(krate), + "external crate mismatch", + )?; + } + + let local = stable_mir::local_crate(); + check_equal( + stable_mir::find_crate(&local.name.as_str()), + Some(local), + "local crate mismatch", + ) +} + +/// Visit all local types, statements and terminator to ensure nothing crashes. +fn check_body(body: stable_mir::mir::Body) { + for bb in body.blocks { + for stmt in bb.statements { + black_box(matches!(stmt, stable_mir::mir::Statement::Assign(..))); + } + black_box(matches!( + bb.terminator, + stable_mir::mir::Terminator::Goto { .. } + )); + } + + for local in body.locals { + black_box(matches!(local.kind(), stable_mir::ty::TyKind::Alias(..))); + } +}