| //@ run-pass |
| // Tests the Stable MIR projections API |
| |
| //@ 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)] |
| |
| 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::crate_def::CrateDef; |
| use stable_mir::mir::{ProjectionElem, Rvalue, StatementKind}; |
| use stable_mir::ty::{RigidTy, TyKind, UintTy}; |
| use stable_mir::ItemKind; |
| use std::assert_matches::assert_matches; |
| use std::io::Write; |
| use std::ops::ControlFlow; |
| |
| const CRATE_NAME: &str = "input"; |
| |
| /// Tests projections within Place objects |
| fn test_place_projections() -> ControlFlow<()> { |
| let items = stable_mir::all_local_items(); |
| let body = get_item(&items, (ItemKind::Fn, "projections")).unwrap().body(); |
| assert_eq!(body.blocks.len(), 4); |
| // The first statement assigns `&s.c` to a local. The projections include a deref for `s`, since |
| // `s` is passed as a reference argument, and a field access for field `c`. |
| match &body.blocks[0].statements[0].kind { |
| StatementKind::Assign( |
| place @ stable_mir::mir::Place { local: _, projection: local_proj }, |
| Rvalue::Ref(_, _, stable_mir::mir::Place { local: _, projection: r_proj }), |
| ) => { |
| // We can't match on vecs, only on slices. Comparing statements for equality wouldn't be |
| // any easier since we'd then have to add in the expected local and region values |
| // instead of matching on wildcards. |
| assert!(local_proj.is_empty()); |
| match &r_proj[..] { |
| // Similarly we can't match against a type, only against its kind. |
| [ProjectionElem::Deref, ProjectionElem::Field(2, ty)] => { |
| assert_matches!( |
| ty.kind(), |
| TyKind::RigidTy(RigidTy::Uint(stable_mir::ty::UintTy::U8)) |
| ); |
| let ty = place.ty(body.locals()).unwrap(); |
| assert_matches!(ty.kind().rigid(), Some(RigidTy::Ref(..))); |
| }, |
| other => panic!( |
| "Unable to match against expected rvalue projection. Expected the projection \ |
| for `s.c`, which is a Deref and u8 Field. Got: {:?}", |
| other |
| ), |
| }; |
| } |
| other => panic!( |
| "Unable to match against expected Assign statement with a Ref rvalue. Expected the \ |
| statement to assign `&s.c` to a local. Got: {:?}", |
| other |
| ), |
| }; |
| // This statement assigns `slice[1]` to a local. The projections include a deref for `slice`, |
| // since `slice` is a reference, and an index. |
| match &body.blocks[2].statements[0].kind { |
| StatementKind::Assign( |
| place @ stable_mir::mir::Place { local: _, projection: local_proj }, |
| Rvalue::Use(stable_mir::mir::Operand::Copy(stable_mir::mir::Place { |
| local: _, |
| projection: r_proj, |
| })), |
| ) => { |
| // We can't match on vecs, only on slices. Comparing for equality wouldn't be any easier |
| // since we'd then have to add in the expected local values instead of matching on |
| // wildcards. |
| assert!(local_proj.is_empty()); |
| assert_matches!(r_proj[..], [ProjectionElem::Deref, ProjectionElem::Index(_)]); |
| let ty = place.ty(body.locals()).unwrap(); |
| assert_matches!(ty.kind().rigid(), Some(RigidTy::Uint(UintTy::U8))); |
| } |
| other => panic!( |
| "Unable to match against expected Assign statement with a Use rvalue. Expected the \ |
| statement to assign `slice[1]` to a local. Got: {:?}", |
| other |
| ), |
| }; |
| // The first terminator gets a slice of an array via the Index operation. Specifically it |
| // performs `&vals[1..3]`. There are no projections in this case, the arguments are just locals. |
| match &body.blocks[0].terminator.kind { |
| stable_mir::mir::TerminatorKind::Call { args, .. } => |
| // We can't match on vecs, only on slices. Comparing for equality wouldn't be any easier |
| // since we'd then have to add in the expected local values instead of matching on |
| // wildcards. |
| { |
| match &args[..] { |
| [ |
| stable_mir::mir::Operand::Move(stable_mir::mir::Place { |
| local: _, |
| projection: arg1_proj, |
| }), |
| stable_mir::mir::Operand::Move(stable_mir::mir::Place { |
| local: _, |
| projection: arg2_proj, |
| }), |
| ] => { |
| assert!(arg1_proj.is_empty()); |
| assert!(arg2_proj.is_empty()); |
| } |
| other => { |
| panic!( |
| "Unable to match against expected arguments to Index call. Expected two \ |
| move operands. Got: {:?}", |
| other |
| ) |
| } |
| } |
| } |
| other => panic!( |
| "Unable to match against expected Call terminator. Expected a terminator that calls \ |
| the Index operation. Got: {:?}", |
| other |
| ), |
| }; |
| |
| ControlFlow::Continue(()) |
| } |
| |
| // Use internal API to find a function in a crate. |
| fn get_item<'a>( |
| items: &'a stable_mir::CrateItems, |
| item: (ItemKind, &str), |
| ) -> Option<&'a stable_mir::CrateItem> { |
| items.iter().find(|crate_item| { |
| crate_item.kind() == item.0 && 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_place_projections).unwrap(); |
| } |
| |
| fn generate_input(path: &str) -> std::io::Result<()> { |
| let mut file = std::fs::File::create(path)?; |
| write!( |
| file, |
| r#" |
| pub struct Struct1 {{ _a: u8, _b: u16, c: u8 }} |
| |
| pub fn projections(s: &Struct1) -> u8 {{ |
| let v = &s.c; |
| let vals = [1, 2, 3, 4]; |
| let slice = &vals[1..3]; |
| v + slice[1] |
| }}"# |
| )?; |
| Ok(()) |
| } |