Skip to content

Commit 893a910

Browse files
committed
Add a test to SMIR body transformation
1 parent c076509 commit 893a910

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
//@ run-pass
2+
//! Test a few methods to transform StableMIR.
3+
4+
//@ ignore-stage1
5+
//@ ignore-cross-compile
6+
//@ ignore-remote
7+
//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837
8+
9+
#![feature(rustc_private)]
10+
#![feature(assert_matches)]
11+
#![feature(control_flow_enum)]
12+
#![feature(ascii_char, ascii_char_variants)]
13+
14+
extern crate rustc_hir;
15+
#[macro_use]
16+
extern crate rustc_smir;
17+
extern crate rustc_driver;
18+
extern crate rustc_interface;
19+
extern crate stable_mir;
20+
21+
use rustc_smir::rustc_internal;
22+
use stable_mir::mir::alloc::GlobalAlloc;
23+
use stable_mir::mir::mono::Instance;
24+
use stable_mir::mir::{Body, Constant, Operand, Rvalue, StatementKind, TerminatorKind};
25+
use stable_mir::ty::{Const, ConstantKind};
26+
use stable_mir::{CrateDef, CrateItems, ItemKind};
27+
use std::convert::TryFrom;
28+
use std::io::Write;
29+
use std::ops::ControlFlow;
30+
31+
const CRATE_NAME: &str = "input";
32+
33+
/// This function uses the Stable MIR APIs to transform the MIR.
34+
fn test_transform() -> ControlFlow<()> {
35+
// Find items in the local crate.
36+
let items = stable_mir::all_local_items();
37+
38+
// Test fn_abi
39+
let target_fn = *get_item(&items, (ItemKind::Fn, "dummy")).unwrap();
40+
let instance = Instance::try_from(target_fn).unwrap();
41+
let body = instance.body().unwrap();
42+
check_msg(&body, "oops");
43+
44+
let new_msg = "new panic message";
45+
let new_body = change_panic_msg(body, new_msg);
46+
check_msg(&new_body, new_msg);
47+
48+
ControlFlow::Continue(())
49+
}
50+
51+
/// Check that the body panic message matches the given message.
52+
fn check_msg(body: &Body, expected: &str) {
53+
let msg = body
54+
.blocks
55+
.iter()
56+
.find_map(|bb| match &bb.terminator.kind {
57+
TerminatorKind::Call { args, .. } => {
58+
assert_eq!(args.len(), 1, "Expected panic message, but found {args:?}");
59+
let msg_const = match &args[0] {
60+
Operand::Constant(msg_const) => msg_const,
61+
Operand::Copy(place) | Operand::Move(place) => {
62+
assert!(place.projection.is_empty());
63+
bb.statements
64+
.iter()
65+
.find_map(|stmt| match &stmt.kind {
66+
StatementKind::Assign(
67+
destination,
68+
Rvalue::Use(Operand::Constant(msg_const)),
69+
) if destination == place => Some(msg_const),
70+
_ => None,
71+
})
72+
.unwrap()
73+
}
74+
};
75+
let ConstantKind::Allocated(alloc) = msg_const.literal.kind() else {
76+
unreachable!()
77+
};
78+
assert_eq!(alloc.provenance.ptrs.len(), 1);
79+
80+
let alloc_prov_id = alloc.provenance.ptrs[0].1 .0;
81+
let GlobalAlloc::Memory(val) = GlobalAlloc::from(alloc_prov_id) else {
82+
unreachable!()
83+
};
84+
let bytes = val.raw_bytes().unwrap();
85+
Some(std::str::from_utf8(&bytes).unwrap().to_string())
86+
}
87+
_ => None,
88+
})
89+
.expect("Failed to find panic message");
90+
assert_eq!(&msg, expected);
91+
}
92+
93+
/// Modify body to use a different panic message.
94+
fn change_panic_msg(mut body: Body, new_msg: &str) -> Body {
95+
for bb in &mut body.blocks {
96+
match &mut bb.terminator.kind {
97+
TerminatorKind::Call { args, .. } => {
98+
let new_const = Const::from_str(new_msg);
99+
args[0] = Operand::Constant(Constant {
100+
literal: new_const,
101+
span: bb.terminator.span,
102+
user_ty: None,
103+
});
104+
}
105+
_ => {}
106+
}
107+
}
108+
body
109+
}
110+
111+
fn get_item<'a>(
112+
items: &'a CrateItems,
113+
item: (ItemKind, &str),
114+
) -> Option<&'a stable_mir::CrateItem> {
115+
items.iter().find(|crate_item| (item.0 == crate_item.kind()) && crate_item.name() == item.1)
116+
}
117+
118+
/// This test will generate and analyze a dummy crate using the stable mir.
119+
/// For that, it will first write the dummy crate into a file.
120+
/// Then it will create a `StableMir` using custom arguments and then
121+
/// it will run the compiler.
122+
fn main() {
123+
let path = "transform_input.rs";
124+
generate_input(&path).unwrap();
125+
let args = vec![
126+
"rustc".to_string(),
127+
"--crate-type=lib".to_string(),
128+
"--crate-name".to_string(),
129+
CRATE_NAME.to_string(),
130+
path.to_string(),
131+
];
132+
run!(args, test_transform).unwrap();
133+
}
134+
135+
fn generate_input(path: &str) -> std::io::Result<()> {
136+
let mut file = std::fs::File::create(path)?;
137+
write!(
138+
file,
139+
r#"
140+
#![feature(panic_internals)]
141+
pub fn dummy() {{
142+
core::panicking::panic_str("oops");
143+
}}
144+
"#
145+
)?;
146+
Ok(())
147+
}

0 commit comments

Comments
 (0)