|
| 1 | +// run-pass |
| 2 | +// Tests the Stable MIR projections API |
| 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 | +// edition: 2021 |
| 9 | + |
| 10 | +#![feature(rustc_private)] |
| 11 | +#![feature(assert_matches)] |
| 12 | +#![feature(control_flow_enum)] |
| 13 | + |
| 14 | +extern crate rustc_hir; |
| 15 | +extern crate rustc_middle; |
| 16 | +#[macro_use] |
| 17 | +extern crate rustc_smir; |
| 18 | +extern crate rustc_driver; |
| 19 | +extern crate rustc_interface; |
| 20 | +extern crate stable_mir; |
| 21 | + |
| 22 | +use rustc_hir::def::DefKind; |
| 23 | +use rustc_middle::ty::TyCtxt; |
| 24 | +use rustc_smir::rustc_internal; |
| 25 | +use stable_mir::mir::{ProjectionElem, Rvalue, StatementKind}; |
| 26 | +use stable_mir::ty::{RigidTy, TyKind}; |
| 27 | +use std::assert_matches::assert_matches; |
| 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 get information about the test crate. |
| 34 | +fn test_projections(_tcx: TyCtxt<'_>) -> ControlFlow<()> { |
| 35 | + // Find items in the local crate. |
| 36 | + let items = stable_mir::all_local_items(); |
| 37 | + |
| 38 | + let body = get_item(&items, (DefKind::Fn, "projections")).unwrap().body(); |
| 39 | + assert_eq!(body.blocks.len(), 4); |
| 40 | + // The first statement assigns `&s.c` to a local. The projections include a deref for `s`, since |
| 41 | + // `s` is passed as a reference argument, and a field access for field `c`. |
| 42 | + match &body.blocks[0].statements[0].kind { |
| 43 | + StatementKind::Assign( |
| 44 | + stable_mir::mir::Place { local: _, projection: local_proj }, |
| 45 | + Rvalue::Ref(_, _, stable_mir::mir::Place { local: _, projection: r_proj }), |
| 46 | + ) => { |
| 47 | + // We can't match on vecs, only on slices. Comparing statements for equality wouldn't be |
| 48 | + // any easier since we'd then have to add in the expected local and region values |
| 49 | + // instead of matching on wildcards. |
| 50 | + assert_matches!(local_proj[..], []); |
| 51 | + match &r_proj[..] { |
| 52 | + // Similarly we can't match against a type, only against its kind. |
| 53 | + [ProjectionElem::Deref, ProjectionElem::Field(2, ty)] => assert_matches!( |
| 54 | + ty.kind(), |
| 55 | + TyKind::RigidTy(RigidTy::Uint(stable_mir::ty::UintTy::U8)) |
| 56 | + ), |
| 57 | + other => panic!( |
| 58 | + "Unable to match against expected rvalue projection. Expected the projection \ |
| 59 | + for `s.c`, which is a Deref and u8 Field. Got: {:?}", |
| 60 | + other |
| 61 | + ), |
| 62 | + }; |
| 63 | + } |
| 64 | + other => panic!( |
| 65 | + "Unable to match against expected Assign statement with a Ref rvalue. Expected the \ |
| 66 | + statement to assign `&s.c` to a local. Got: {:?}", |
| 67 | + other |
| 68 | + ), |
| 69 | + }; |
| 70 | + // This statement assigns `slice[1]` to a local. The projections include a deref for `slice`, |
| 71 | + // since `slice` is a reference, and an index. |
| 72 | + match &body.blocks[2].statements[0].kind { |
| 73 | + StatementKind::Assign( |
| 74 | + stable_mir::mir::Place { local: _, projection: local_proj }, |
| 75 | + Rvalue::Use(stable_mir::mir::Operand::Copy(stable_mir::mir::Place { |
| 76 | + local: _, |
| 77 | + projection: r_proj, |
| 78 | + })), |
| 79 | + ) => { |
| 80 | + // We can't match on vecs, only on slices. Comparing for equality wouldn't be any easier |
| 81 | + // since we'd then have to add in the expected local values instead of matching on |
| 82 | + // wildcards. |
| 83 | + assert_matches!(local_proj[..], []); |
| 84 | + assert_matches!(r_proj[..], [ProjectionElem::Deref, ProjectionElem::Index(_)]); |
| 85 | + } |
| 86 | + other => panic!( |
| 87 | + "Unable to match against expected Assign statement with a Use rvalue. Expected the \ |
| 88 | + statement to assign `slice[1]` to a local. Got: {:?}", |
| 89 | + other |
| 90 | + ), |
| 91 | + }; |
| 92 | + // The first terminator gets a slice of an array via the Index operation. Specifically it |
| 93 | + // performs `&vals[1..3]`. There are no projections in this case, the arguments are just locals. |
| 94 | + match &body.blocks[0].terminator.kind { |
| 95 | + stable_mir::mir::TerminatorKind::Call { args, .. } => |
| 96 | + // We can't match on vecs, only on slices. Comparing for equality wouldn't be any easier |
| 97 | + // since we'd then have to add in the expected local values instead of matching on |
| 98 | + // wildcards. |
| 99 | + { |
| 100 | + match &args[..] { |
| 101 | + [ |
| 102 | + stable_mir::mir::Operand::Move(stable_mir::mir::Place { |
| 103 | + local: _, |
| 104 | + projection: arg1_proj, |
| 105 | + }), |
| 106 | + stable_mir::mir::Operand::Move(stable_mir::mir::Place { |
| 107 | + local: _, |
| 108 | + projection: arg2_proj, |
| 109 | + }), |
| 110 | + ] => { |
| 111 | + assert_matches!(arg1_proj[..], []); |
| 112 | + assert_matches!(arg2_proj[..], []); |
| 113 | + } |
| 114 | + other => { |
| 115 | + panic!( |
| 116 | + "Unable to match against expected arguments to Index call. Expected two \ |
| 117 | + move operands. Got: {:?}", |
| 118 | + other |
| 119 | + ) |
| 120 | + } |
| 121 | + } |
| 122 | + } |
| 123 | + other => panic!( |
| 124 | + "Unable to match against expected Call terminator. Expected a terminator that calls \ |
| 125 | + the Index operation. Got: {:?}", |
| 126 | + other |
| 127 | + ), |
| 128 | + }; |
| 129 | + |
| 130 | + ControlFlow::Continue(()) |
| 131 | +} |
| 132 | + |
| 133 | +// Use internal API to find a function in a crate. |
| 134 | +fn get_item<'a>( |
| 135 | + items: &'a stable_mir::CrateItems, |
| 136 | + item: (DefKind, &str), |
| 137 | +) -> Option<&'a stable_mir::CrateItem> { |
| 138 | + items.iter().find(|crate_item| { |
| 139 | + crate_item.kind().to_string() == format!("{:?}", item.0) && crate_item.name() == item.1 |
| 140 | + }) |
| 141 | +} |
| 142 | + |
| 143 | +/// This test will generate and analyze a dummy crate using the stable mir. |
| 144 | +/// For that, it will first write the dummy crate into a file. |
| 145 | +/// Then it will create a `StableMir` using custom arguments and then |
| 146 | +/// it will run the compiler. |
| 147 | +fn main() { |
| 148 | + let path = "input.rs"; |
| 149 | + generate_input(&path).unwrap(); |
| 150 | + let args = vec![ |
| 151 | + "rustc".to_string(), |
| 152 | + "--crate-type=lib".to_string(), |
| 153 | + "--crate-name".to_string(), |
| 154 | + CRATE_NAME.to_string(), |
| 155 | + path.to_string(), |
| 156 | + ]; |
| 157 | + run!(args, tcx, test_projections(tcx)).unwrap(); |
| 158 | +} |
| 159 | + |
| 160 | +fn generate_input(path: &str) -> std::io::Result<()> { |
| 161 | + let mut file = std::fs::File::create(path)?; |
| 162 | + write!( |
| 163 | + file, |
| 164 | + r#" |
| 165 | + pub struct Struct1 {{ _a: u8, _b: u16, c: u8 }} |
| 166 | +
|
| 167 | + pub fn projections(s: &Struct1) -> u8 {{ |
| 168 | + let v = &s.c; |
| 169 | + let vals = [1, 2, 3, 4]; |
| 170 | + let slice = &vals[1..3]; |
| 171 | + v + slice[1] |
| 172 | + }}"# |
| 173 | + )?; |
| 174 | + Ok(()) |
| 175 | +} |
0 commit comments