blob: 1d3e4c6845ba8ba9f31f724ef6fde87d2e685db8 [file] [log] [blame]
//@ run-pass
//! Test a few methods to transform StableMIR.
//@ ignore-stage1
//@ ignore-cross-compile
//@ ignore-remote
//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837
#![feature(rustc_private)]
#![feature(assert_matches)]
#![feature(control_flow_enum)]
#![feature(ascii_char, ascii_char_variants)]
extern crate rustc_hir;
#[macro_use]
extern crate rustc_smir;
extern crate rustc_driver;
extern crate rustc_interface;
extern crate stable_mir;
use rustc_smir::rustc_internal;
use stable_mir::mir::alloc::GlobalAlloc;
use stable_mir::mir::mono::Instance;
use stable_mir::mir::{Body, ConstOperand, Operand, Rvalue, StatementKind, TerminatorKind};
use stable_mir::ty::{ConstantKind, MirConst};
use stable_mir::{CrateDef, CrateItems, ItemKind};
use std::convert::TryFrom;
use std::io::Write;
use std::ops::ControlFlow;
const CRATE_NAME: &str = "input";
/// This function uses the Stable MIR APIs to transform the MIR.
fn test_transform() -> ControlFlow<()> {
// Find items in the local crate.
let items = stable_mir::all_local_items();
// Test fn_abi
let target_fn = *get_item(&items, (ItemKind::Fn, "dummy")).unwrap();
let instance = Instance::try_from(target_fn).unwrap();
let body = instance.body().unwrap();
check_msg(&body, "oops");
let new_msg = "new panic message";
let new_body = change_panic_msg(body, new_msg);
check_msg(&new_body, new_msg);
ControlFlow::Continue(())
}
/// Check that the body panic message matches the given message.
fn check_msg(body: &Body, expected: &str) {
let msg = body
.blocks
.iter()
.find_map(|bb| match &bb.terminator.kind {
TerminatorKind::Call { args, .. } => {
assert_eq!(args.len(), 1, "Expected panic message, but found {args:?}");
let msg_const = match &args[0] {
Operand::Constant(msg_const) => msg_const,
Operand::Copy(place) | Operand::Move(place) => {
assert!(place.projection.is_empty());
bb.statements
.iter()
.find_map(|stmt| match &stmt.kind {
StatementKind::Assign(
destination,
Rvalue::Use(Operand::Constant(msg_const)),
) if destination == place => Some(msg_const),
_ => None,
})
.unwrap()
}
};
let ConstantKind::Allocated(alloc) = msg_const.const_.kind() else {
unreachable!()
};
assert_eq!(alloc.provenance.ptrs.len(), 1);
let alloc_prov_id = alloc.provenance.ptrs[0].1.0;
let GlobalAlloc::Memory(val) = GlobalAlloc::from(alloc_prov_id) else {
unreachable!()
};
let bytes = val.raw_bytes().unwrap();
Some(std::str::from_utf8(&bytes).unwrap().to_string())
}
_ => None,
})
.expect("Failed to find panic message");
assert_eq!(&msg, expected);
}
/// Modify body to use a different panic message.
fn change_panic_msg(mut body: Body, new_msg: &str) -> Body {
for bb in &mut body.blocks {
match &mut bb.terminator.kind {
TerminatorKind::Call { args, .. } => {
let new_const = MirConst::from_str(new_msg);
args[0] = Operand::Constant(ConstOperand {
const_: new_const,
span: bb.terminator.span,
user_ty: None,
});
}
_ => {}
}
}
body
}
fn get_item<'a>(
items: &'a CrateItems,
item: (ItemKind, &str),
) -> Option<&'a stable_mir::CrateItem> {
items.iter().find(|crate_item| (item.0 == crate_item.kind()) && crate_item.name() == item.1)
}
/// This test will generate and analyze a dummy crate using the stable mir.
/// For that, it will first write the dummy crate into a file.
/// Then it will create a `StableMir` using custom arguments and then
/// it will run the compiler.
fn main() {
let path = "transform_input.rs";
generate_input(&path).unwrap();
let args = vec![
"rustc".to_string(),
"--crate-type=lib".to_string(),
"--crate-name".to_string(),
CRATE_NAME.to_string(),
path.to_string(),
];
run!(args, test_transform).unwrap();
}
fn generate_input(path: &str) -> std::io::Result<()> {
let mut file = std::fs::File::create(path)?;
write!(
file,
r#"
fn panic_str(msg: &str) {{ panic!("{{}}", msg); }}
pub fn dummy() {{
panic_str("oops");
}}
"#
)?;
Ok(())
}