1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
//@ 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, Constant, Operand, Rvalue, StatementKind, TerminatorKind};
use stable_mir::ty::{Const, ConstantKind};
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.literal.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 = Const::from_str(new_msg);
args[0] = Operand::Constant(Constant {
literal: 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#"
#![feature(panic_internals)]
pub fn dummy() {{
core::panicking::panic_str("oops");
}}
"#
)?;
Ok(())
}
|