// run-pass // Test that users are able to use stable mir APIs to retrieve information of the current crate // ignore-stage1 // ignore-cross-compile // ignore-remote // ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 // edition: 2021 #![feature(rustc_private)] #![feature(assert_matches)] #![feature(control_flow_enum)] extern crate rustc_hir; #[macro_use] extern crate rustc_smir; extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; use rustc_hir::def::DefKind; use rustc_smir::rustc_internal; use stable_mir::ItemKind; use stable_mir::crate_def::CrateDef; use stable_mir::mir::mono::Instance; use stable_mir::ty::{RigidTy, TyKind}; use std::assert_matches::assert_matches; use std::io::Write; use std::ops::ControlFlow; const CRATE_NAME: &str = "input"; /// This function uses the Stable MIR APIs to get information about the test crate. fn test_stable_mir() -> ControlFlow<()> { // Get the local crate using stable_mir API. let local = stable_mir::local_crate(); assert_eq!(&local.name, CRATE_NAME); assert_eq!(stable_mir::entry_fn(), None); // Find items in the local crate. let items = stable_mir::all_local_items(); assert!(get_item(&items, (DefKind::Fn, "foo::bar")).is_some()); // Find the `std` crate and assert that there is only one of it. assert!(stable_mir::find_crates("std").len() == 1); let bar = get_item(&items, (DefKind::Fn, "bar")).unwrap(); let body = bar.body(); assert_eq!(body.locals().len(), 2); assert_eq!(body.blocks.len(), 1); let block = &body.blocks[0]; assert_eq!(block.statements.len(), 1); match &block.statements[0].kind { stable_mir::mir::StatementKind::Assign(..) => {} other => panic!("{other:?}"), } match &block.terminator.kind { stable_mir::mir::TerminatorKind::Return => {} other => panic!("{other:?}"), } let foo_bar = get_item(&items, (DefKind::Fn, "foo_bar")).unwrap(); let body = foo_bar.body(); assert_eq!(body.locals().len(), 5); assert_eq!(body.blocks.len(), 4); let block = &body.blocks[0]; match &block.terminator.kind { stable_mir::mir::TerminatorKind::Call { .. } => {} other => panic!("{other:?}"), } let types = get_item(&items, (DefKind::Fn, "types")).unwrap(); let body = types.body(); assert_eq!(body.locals().len(), 6); assert_matches!( body.locals()[0].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Bool) ); assert_matches!( body.locals()[1].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Bool) ); assert_matches!( body.locals()[2].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Char) ); assert_matches!( body.locals()[3].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Int(stable_mir::ty::IntTy::I32)) ); assert_matches!( body.locals()[4].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Uint(stable_mir::ty::UintTy::U64)) ); assert_matches!( body.locals()[5].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Float( stable_mir::ty::FloatTy::F64 )) ); let drop = get_item(&items, (DefKind::Fn, "drop")).unwrap(); let body = drop.body(); assert_eq!(body.blocks.len(), 2); let block = &body.blocks[0]; match &block.terminator.kind { stable_mir::mir::TerminatorKind::Drop { .. } => {} other => panic!("{other:?}"), } let assert = get_item(&items, (DefKind::Fn, "assert")).unwrap(); let body = assert.body(); assert_eq!(body.blocks.len(), 2); let block = &body.blocks[0]; match &block.terminator.kind { stable_mir::mir::TerminatorKind::Assert { .. } => {} other => panic!("{other:?}"), } let monomorphic = get_item(&items, (DefKind::Fn, "monomorphic")).unwrap(); let instance = Instance::try_from(monomorphic.clone()).unwrap(); for block in instance.body().unwrap().blocks { match &block.terminator.kind { stable_mir::mir::TerminatorKind::Call { func, .. } => { let TyKind::RigidTy(ty) = func.ty(&body.locals()).unwrap().kind() else { unreachable!() }; let RigidTy::FnDef(def, args) = ty else { unreachable!() }; let next_func = Instance::resolve(def, &args).unwrap(); match next_func.body().unwrap().locals()[1].ty.kind() { TyKind::RigidTy(RigidTy::Uint(_)) | TyKind::RigidTy(RigidTy::Tuple(_)) => {} other => panic!("{other:?}"), } } stable_mir::mir::TerminatorKind::Return => {} other => panic!("{other:?}"), } } let foo_const = get_item(&items, (DefKind::Const, "FOO")).unwrap(); // Ensure we don't panic trying to get the body of a constant. foo_const.body(); let locals_fn = get_item(&items, (DefKind::Fn, "locals")).unwrap(); let body = locals_fn.body(); assert_eq!(body.locals().len(), 4); assert_matches!( body.ret_local().ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Char) ); assert_eq!(body.arg_locals().len(), 2); assert_matches!( body.arg_locals()[0].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Int(stable_mir::ty::IntTy::I32)) ); assert_matches!( body.arg_locals()[1].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Uint(stable_mir::ty::UintTy::U64)) ); assert_eq!(body.inner_locals().len(), 1); // If conditions have an extra inner local to hold their results assert_matches!( body.inner_locals()[0].ty.kind(), stable_mir::ty::TyKind::RigidTy(stable_mir::ty::RigidTy::Bool) ); ControlFlow::Continue(()) } // Use internal API to find a function in a crate. fn get_item<'a>( items: &'a stable_mir::CrateItems, item: (DefKind, &str), ) -> Option<&'a stable_mir::CrateItem> { items.iter().find(|crate_item| { matches!((item.0, crate_item.kind()), (DefKind::Fn, ItemKind::Fn) | (DefKind::Const, ItemKind::Const)) && 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 = "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_stable_mir).unwrap(); } fn generate_input(path: &str) -> std::io::Result<()> { let mut file = std::fs::File::create(path)?; write!( file, r#" pub const FOO: u32 = 1 + 2; fn generic(t: T) -> [(); U] {{ _ = t; [(); U] }} pub fn monomorphic() {{ generic::<(), 5>(()); generic::(45); }} mod foo {{ pub fn bar(i: i32) -> i64 {{ i as i64 }} }} pub fn bar(x: i32) -> i32 {{ x }} pub fn foo_bar(x: i32, y: i32) -> i64 {{ let x_64 = foo::bar(x); let y_64 = foo::bar(y); x_64.wrapping_add(y_64) }} pub fn types(b: bool, _: char, _: i32, _: u64, _: f64) -> bool {{ b }} pub fn drop(_: String) {{}} pub fn assert(x: i32) -> i32 {{ x + 1 }} pub fn locals(a: i32, _: u64) -> char {{ if a > 5 {{ 'a' }} else {{ 'b' }} }}"# )?; Ok(()) }