summaryrefslogtreecommitdiffstats
path: root/tests/ui-fulldeps/stable-mir
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:25:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:25:56 +0000
commit018c4950b9406055dec02ef0fb52f132e2bb1e2c (patch)
treea835ebdf2088ef88fa681f8fad45f09922c1ae9a /tests/ui-fulldeps/stable-mir
parentAdding debian version 1.75.0+dfsg1-5. (diff)
downloadrustc-018c4950b9406055dec02ef0fb52f132e2bb1e2c.tar.xz
rustc-018c4950b9406055dec02ef0fb52f132e2bb1e2c.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/ui-fulldeps/stable-mir')
-rw-r--r--tests/ui-fulldeps/stable-mir/check_abi.rs143
-rw-r--r--tests/ui-fulldeps/stable-mir/check_allocation.rs277
-rw-r--r--tests/ui-fulldeps/stable-mir/check_defs.rs132
-rw-r--r--tests/ui-fulldeps/stable-mir/check_instance.rs21
-rw-r--r--tests/ui-fulldeps/stable-mir/check_item_kind.rs84
-rw-r--r--tests/ui-fulldeps/stable-mir/check_ty_fold.rs115
-rw-r--r--tests/ui-fulldeps/stable-mir/crate-info.rs12
-rw-r--r--tests/ui-fulldeps/stable-mir/projections.rs180
-rw-r--r--tests/ui-fulldeps/stable-mir/smir_visitor.rs5
9 files changed, 959 insertions, 10 deletions
diff --git a/tests/ui-fulldeps/stable-mir/check_abi.rs b/tests/ui-fulldeps/stable-mir/check_abi.rs
new file mode 100644
index 000000000..30b42bc3b
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/check_abi.rs
@@ -0,0 +1,143 @@
+// run-pass
+//! Test information regarding type layout.
+
+// 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;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_smir;
+extern crate rustc_driver;
+extern crate rustc_interface;
+extern crate stable_mir;
+
+use rustc_middle::ty::TyCtxt;
+use rustc_smir::rustc_internal;
+use stable_mir::abi::{ArgAbi, CallConvention, FieldsShape, PassMode, VariantsShape};
+use stable_mir::mir::mono::Instance;
+use stable_mir::{CrateDef, CrateItem, CrateItems, ItemKind};
+use std::assert_matches::assert_matches;
+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 get information about the test crate.
+fn test_stable_mir(_tcx: TyCtxt<'_>) -> 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, "fn_abi")).unwrap();
+ let instance = Instance::try_from(target_fn).unwrap();
+ let fn_abi = instance.fn_abi().unwrap();
+ assert_eq!(fn_abi.conv, CallConvention::Rust);
+ assert_eq!(fn_abi.args.len(), 2);
+
+ check_ignore(&fn_abi.args[0]);
+ check_primitive(&fn_abi.args[1]);
+ check_result(fn_abi.ret);
+
+ // Test variadic function.
+ let variadic_fn = *get_item(&items, (ItemKind::Fn, "variadic_fn")).unwrap();
+ check_variadic(variadic_fn);
+
+ ControlFlow::Continue(())
+}
+
+/// Check the variadic function ABI:
+/// ```no_run
+/// pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) -> usize {
+/// 0
+/// }
+/// ```
+fn check_variadic(variadic_fn: CrateItem) {
+ let instance = Instance::try_from(variadic_fn).unwrap();
+ let abi = instance.fn_abi().unwrap();
+ assert!(abi.c_variadic);
+ assert_eq!(abi.args.len(), 1);
+}
+
+/// Check the argument to be ignored: `ignore: [u8; 0]`.
+fn check_ignore(abi: &ArgAbi) {
+ assert!(abi.ty.kind().is_array());
+ assert_eq!(abi.mode, PassMode::Ignore);
+ let layout = abi.layout.shape();
+ assert!(layout.is_sized());
+ assert!(layout.is_1zst());
+}
+
+/// Check the primitive argument: `primitive: char`.
+fn check_primitive(abi: &ArgAbi) {
+ assert!(abi.ty.kind().is_char());
+ assert_matches!(abi.mode, PassMode::Direct(_));
+ let layout = abi.layout.shape();
+ assert!(layout.is_sized());
+ assert!(!layout.is_1zst());
+ assert_matches!(layout.fields, FieldsShape::Primitive);
+}
+
+/// Check the return value: `Result<usize, &str>`.
+fn check_result(abi: ArgAbi) {
+ assert!(abi.ty.kind().is_enum());
+ assert_matches!(abi.mode, PassMode::Indirect { .. });
+ let layout = abi.layout.shape();
+ assert!(layout.is_sized());
+ assert_matches!(layout.fields, FieldsShape::Arbitrary { .. });
+ assert_matches!(layout.variants, VariantsShape::Multiple { .. })
+}
+
+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 = "alloc_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, tcx, test_stable_mir(tcx)).unwrap();
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+ let mut file = std::fs::File::create(path)?;
+ write!(
+ file,
+ r#"
+ #![feature(c_variadic)]
+ #![allow(unused_variables)]
+
+ pub fn fn_abi(ignore: [u8; 0], primitive: char) -> Result<usize, &'static str> {{
+ // We only care about the signature.
+ todo!()
+ }}
+
+
+ pub unsafe extern "C" fn variadic_fn(n: usize, mut args: ...) -> usize {{
+ 0
+ }}
+ "#
+ )?;
+ Ok(())
+}
diff --git a/tests/ui-fulldeps/stable-mir/check_allocation.rs b/tests/ui-fulldeps/stable-mir/check_allocation.rs
new file mode 100644
index 000000000..b13f76452
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/check_allocation.rs
@@ -0,0 +1,277 @@
+// run-pass
+//! Test that users are able to use stable mir APIs to retrieve information of global allocations
+//! such as `vtable_allocation`.
+
+// 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)]
+#![feature(ascii_char, ascii_char_variants)]
+#![feature(c_str_literals)]
+
+extern crate rustc_hir;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_smir;
+extern crate rustc_driver;
+extern crate rustc_interface;
+extern crate stable_mir;
+
+use rustc_middle::ty::TyCtxt;
+use rustc_smir::rustc_internal;
+use stable_mir::crate_def::CrateDef;
+use stable_mir::mir::alloc::GlobalAlloc;
+use stable_mir::mir::mono::{Instance, InstanceKind, StaticDef};
+use stable_mir::mir::{Body, TerminatorKind};
+use stable_mir::ty::{Allocation, ConstantKind, RigidTy, TyKind};
+use stable_mir::{CrateItem, CrateItems, ItemKind};
+use std::ascii::Char;
+use std::assert_matches::assert_matches;
+use std::cmp::{max, min};
+use std::collections::HashMap;
+use std::ffi::CStr;
+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(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
+ // Find items in the local crate.
+ let items = stable_mir::all_local_items();
+ check_foo(*get_item(&items, (ItemKind::Static, "FOO")).unwrap());
+ check_bar(*get_item(&items, (ItemKind::Static, "BAR")).unwrap());
+ check_len(*get_item(&items, (ItemKind::Static, "LEN")).unwrap());
+ check_cstr(*get_item(&items, (ItemKind::Static, "C_STR")).unwrap());
+ check_other_consts(*get_item(&items, (ItemKind::Fn, "other_consts")).unwrap());
+ check_type_id(*get_item(&items, (ItemKind::Fn, "check_type_id")).unwrap());
+ ControlFlow::Continue(())
+}
+
+/// Check the allocation data for static `FOO`.
+///
+/// ```no_run
+/// static FOO: [&str; 2] = ["hi", "there"];
+/// ```
+fn check_foo(item: CrateItem) {
+ let def = StaticDef::try_from(item).unwrap();
+ let alloc = def.eval_initializer().unwrap();
+ assert_eq!(alloc.provenance.ptrs.len(), 2);
+
+ let alloc_id_0 = alloc.provenance.ptrs[0].1.0;
+ assert_matches!(GlobalAlloc::from(alloc_id_0), GlobalAlloc::Memory(..));
+
+ let alloc_id_1 = alloc.provenance.ptrs[1].1.0;
+ assert_matches!(GlobalAlloc::from(alloc_id_1), GlobalAlloc::Memory(..));
+}
+
+/// Check the allocation data for static `BAR`.
+///
+/// ```no_run
+/// static BAR: &str = "Bar";
+/// ```
+fn check_bar(item: CrateItem) {
+ let def = StaticDef::try_from(item).unwrap();
+ let alloc = def.eval_initializer().unwrap();
+ assert_eq!(alloc.provenance.ptrs.len(), 1);
+
+ let alloc_id_0 = alloc.provenance.ptrs[0].1.0;
+ let GlobalAlloc::Memory(allocation) = GlobalAlloc::from(alloc_id_0) else { unreachable!() };
+ assert_eq!(allocation.bytes.len(), 3);
+ assert_eq!(allocation.bytes[0].unwrap(), Char::CapitalB.to_u8());
+ assert_eq!(allocation.bytes[1].unwrap(), Char::SmallA.to_u8());
+ assert_eq!(allocation.bytes[2].unwrap(), Char::SmallR.to_u8());
+ assert_eq!(std::str::from_utf8(&allocation.raw_bytes().unwrap()), Ok("Bar"));
+}
+
+/// Check the allocation data for static `C_STR`.
+///
+/// ```no_run
+/// static C_STR: &core::ffi::cstr = c"cstr";
+/// ```
+fn check_cstr(item: CrateItem) {
+ let def = StaticDef::try_from(item).unwrap();
+ let alloc = def.eval_initializer().unwrap();
+ assert_eq!(alloc.provenance.ptrs.len(), 1);
+ let deref = item.ty().kind().builtin_deref(true).unwrap();
+ assert!(deref.ty.kind().is_cstr(), "Expected CStr, but got: {:?}", item.ty());
+
+ let alloc_id_0 = alloc.provenance.ptrs[0].1.0;
+ let GlobalAlloc::Memory(allocation) = GlobalAlloc::from(alloc_id_0) else { unreachable!() };
+ assert_eq!(allocation.bytes.len(), 5);
+ assert_eq!(CStr::from_bytes_until_nul(&allocation.raw_bytes().unwrap()), Ok(c"cstr"));
+}
+
+/// Check the allocation data for constants used in `other_consts` function.
+fn check_other_consts(item: CrateItem) {
+ // Instance body will force constant evaluation.
+ let body = Instance::try_from(item).unwrap().body().unwrap();
+ let assigns = collect_consts(&body);
+ assert_eq!(assigns.len(), 9);
+ for (name, alloc) in assigns {
+ match name.as_str() {
+ "_max_u128" => {
+ assert_eq!(alloc.read_uint(), Ok(u128::MAX), "Failed parsing allocation: {alloc:?}")
+ }
+ "_min_i128" => {
+ assert_eq!(alloc.read_int(), Ok(i128::MIN), "Failed parsing allocation: {alloc:?}")
+ }
+ "_max_i8" => {
+ assert_eq!(
+ alloc.read_int().unwrap() as i8,
+ i8::MAX,
+ "Failed parsing allocation: {alloc:?}"
+ )
+ }
+ "_char" => {
+ assert_eq!(
+ char::from_u32(alloc.read_uint().unwrap() as u32),
+ Some('x'),
+ "Failed parsing allocation: {alloc:?}"
+ )
+ }
+ "_false" => {
+ assert_eq!(alloc.read_bool(), Ok(false), "Failed parsing allocation: {alloc:?}")
+ }
+ "_true" => {
+ assert_eq!(alloc.read_bool(), Ok(true), "Failed parsing allocation: {alloc:?}")
+ }
+ "_ptr" => {
+ assert_eq!(alloc.is_null(), Ok(false), "Failed parsing allocation: {alloc:?}")
+ }
+ "_null_ptr" => {
+ assert_eq!(alloc.is_null(), Ok(true), "Failed parsing allocation: {alloc:?}")
+ }
+ "_tuple" => {
+ // The order of fields is not guaranteed.
+ let first = alloc.read_partial_uint(0..4).unwrap();
+ let second = alloc.read_partial_uint(4..8).unwrap();
+ assert_eq!(max(first, second) as u32, u32::MAX);
+ assert_eq!(min(first, second), 10);
+ }
+ _ => {
+ unreachable!("{name} -- {alloc:?}")
+ }
+ }
+ }
+}
+
+/// Check that we can retrieve the type id of char and bool, and that they have different values.
+fn check_type_id(item: CrateItem) {
+ let body = Instance::try_from(item).unwrap().body().unwrap();
+ let mut ids: Vec<u128> = vec![];
+ for term in body.blocks.iter().map(|bb| &bb.terminator) {
+ match &term.kind {
+ TerminatorKind::Call { func, destination, .. } => {
+ let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else {
+ unreachable!()
+ };
+ let RigidTy::FnDef(def, args) = ty else { unreachable!() };
+ let instance = Instance::resolve(def, &args).unwrap();
+ assert_eq!(instance.kind, InstanceKind::Intrinsic);
+ let dest_ty = destination.ty(body.locals()).unwrap();
+ let alloc = instance.try_const_eval(dest_ty).unwrap();
+ ids.push(alloc.read_uint().unwrap());
+ }
+ _ => { /* Do nothing */ }
+ }
+ }
+ assert_eq!(ids.len(), 2);
+ assert_ne!(ids[0], ids[1]);
+}
+
+/// Collects all the constant assignments.
+pub fn collect_consts(body: &Body) -> HashMap<String, &Allocation> {
+ body.var_debug_info
+ .iter()
+ .filter_map(|info| {
+ info.constant().map(|const_op| {
+ let ConstantKind::Allocated(alloc) = const_op.const_.kind() else { unreachable!() };
+ (info.name.clone(), alloc)
+ })
+ })
+ .collect::<HashMap<_, _>>()
+}
+
+/// Check the allocation data for `LEN`.
+///
+/// ```no_run
+/// static LEN: usize = 2;
+/// ```
+fn check_len(item: CrateItem) {
+ let def = StaticDef::try_from(item).unwrap();
+ let alloc = def.eval_initializer().unwrap();
+ assert!(alloc.provenance.ptrs.is_empty());
+ assert_eq!(alloc.read_uint(), Ok(2));
+}
+
+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 = "alloc_input.rs";
+ generate_input(&path).unwrap();
+ let args = vec![
+ "rustc".to_string(),
+ "--edition=2021".to_string(),
+ "--crate-name".to_string(),
+ CRATE_NAME.to_string(),
+ path.to_string(),
+ ];
+ run!(args, tcx, test_stable_mir(tcx)).unwrap();
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+ let mut file = std::fs::File::create(path)?;
+ write!(
+ file,
+ r#"
+ #![feature(core_intrinsics)]
+ #![feature(c_str_literals)]
+ use std::intrinsics::type_id;
+
+ static LEN: usize = 2;
+ static FOO: [&str; 2] = ["hi", "there"];
+ static BAR: &str = "Bar";
+ static C_STR: &std::ffi::CStr = c"cstr";
+ const NULL: *const u8 = std::ptr::null();
+ const TUPLE: (u32, u32) = (10, u32::MAX);
+
+ fn other_consts() {{
+ let _max_u128 = u128::MAX;
+ let _min_i128 = i128::MIN;
+ let _max_i8 = i8::MAX;
+ let _char = 'x';
+ let _false = false;
+ let _true = true;
+ let _ptr = &BAR;
+ let _null_ptr: *const u8 = NULL;
+ let _tuple = TUPLE;
+ }}
+
+ fn check_type_id() {{
+ let _char_id = type_id::<char>();
+ let _bool_id = type_id::<bool>();
+ }}
+
+ pub fn main() {{
+ println!("{{FOO:?}}! {{BAR}}");
+ assert_eq!(FOO.len(), LEN);
+ other_consts();
+ }}"#
+ )?;
+ Ok(())
+}
diff --git a/tests/ui-fulldeps/stable-mir/check_defs.rs b/tests/ui-fulldeps/stable-mir/check_defs.rs
new file mode 100644
index 000000000..e9a2599d8
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/check_defs.rs
@@ -0,0 +1,132 @@
+// run-pass
+//! Test that users are able to use stable mir APIs to retrieve information about crate definitions.
+
+// 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_middle;
+#[macro_use]
+extern crate rustc_smir;
+extern crate rustc_driver;
+extern crate rustc_interface;
+extern crate stable_mir;
+
+use std::assert_matches::assert_matches;
+use mir::{mono::Instance, TerminatorKind::*};
+use rustc_middle::ty::TyCtxt;
+use rustc_smir::rustc_internal;
+use stable_mir::ty::{RigidTy, TyKind, Ty, UintTy};
+use stable_mir::*;
+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(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
+ let entry = stable_mir::entry_fn().unwrap();
+ let main_fn = Instance::try_from(entry).unwrap();
+ assert_eq!(main_fn.name(), "main");
+ assert_eq!(main_fn.trimmed_name(), "main");
+
+ let instances = get_instances(main_fn.body().unwrap());
+ assert_eq!(instances.len(), 2);
+ test_fn(instances[0], "from_u32", "std::char::from_u32", "core");
+ test_fn(instances[1], "Vec::<u8>::new", "std::vec::Vec::<u8>::new", "alloc");
+ test_vec_new(instances[1]);
+ ControlFlow::Continue(())
+}
+
+fn test_fn(instance: Instance, expected_trimmed: &str, expected_qualified: &str, krate: &str) {
+ let trimmed = instance.trimmed_name();
+ let qualified = instance.name();
+ assert_eq!(&trimmed, expected_trimmed);
+ assert_eq!(&qualified, expected_qualified);
+
+ let item = CrateItem::try_from(instance).unwrap();
+ let trimmed = item.trimmed_name();
+ let qualified = item.name();
+ assert_eq!(trimmed, expected_trimmed.replace("u8", "T"));
+ assert_eq!(qualified, expected_qualified.replace("u8", "T"));
+ assert_eq!(&item.krate().name, krate);
+}
+
+fn extract_elem_ty(ty: Ty) -> Ty {
+ match ty.kind() {
+ TyKind::RigidTy(RigidTy::Adt(_, args)) => {
+ *args.0[0].expect_ty()
+ }
+ _ => unreachable!("Expected Vec ADT, but found: {ty:?}")
+ }
+}
+
+/// Check signature and type of `Vec::<u8>::new` and its generic version.
+fn test_vec_new(instance: mir::mono::Instance) {
+ let sig = instance.fn_abi().unwrap();
+ assert_eq!(&sig.args, &[]);
+ let elem_ty = extract_elem_ty(sig.ret.ty);
+ assert_matches!(elem_ty.kind(), TyKind::RigidTy(RigidTy::Uint(UintTy::U8)));
+
+ // Get the signature for Vec::<T>::new.
+ let item = CrateItem::try_from(instance).unwrap();
+ let ty = item.ty();
+ let gen_sig = ty.kind().fn_sig().unwrap().skip_binder();
+ let gen_ty = extract_elem_ty(gen_sig.output());
+ assert_matches!(gen_ty.kind(), TyKind::Param(_));
+}
+
+/// Inspect the instance body
+fn get_instances(body: mir::Body) -> Vec<Instance> {
+ body.blocks.iter().filter_map(|bb| {
+ match &bb.terminator.kind {
+ Call { func, .. } => {
+ let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { unreachable!
+ () };
+ let RigidTy::FnDef(def, args) = ty else { unreachable!() };
+ Instance::resolve(def, &args).ok()
+ }
+ _ => {
+ None
+ }
+ }
+ }).collect::<Vec<_>>()
+}
+
+/// 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 = "defs_input.rs";
+ generate_input(&path).unwrap();
+ let args = vec![
+ "rustc".to_string(),
+ "-Cpanic=abort".to_string(),
+ "--crate-name".to_string(),
+ CRATE_NAME.to_string(),
+ path.to_string(),
+ ];
+ run!(args, tcx, test_stable_mir(tcx)).unwrap();
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+ let mut file = std::fs::File::create(path)?;
+ write!(
+ file,
+ r#"
+
+ fn main() {{
+ let _c = core::char::from_u32(99);
+ let _v = Vec::<u8>::new();
+ }}
+ "#
+ )?;
+ Ok(())
+}
diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs
index a34087775..5cb07eabf 100644
--- a/tests/ui-fulldeps/stable-mir/check_instance.rs
+++ b/tests/ui-fulldeps/stable-mir/check_instance.rs
@@ -49,7 +49,7 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
assert!(generic.iter().all(|item| mir::mono::Instance::try_from(*item).is_err()));
for instance in instances {
- test_body(instance.body())
+ test_body(instance.body().unwrap())
}
ControlFlow::Continue(())
}
@@ -59,10 +59,17 @@ fn test_body(body: mir::Body) {
for term in body.blocks.iter().map(|bb| &bb.terminator) {
match &term.kind {
Call { func, .. } => {
- let TyKind::RigidTy(ty) = func.ty(body.locals()).kind() else { unreachable!() };
+ let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { unreachable!
+ () };
let RigidTy::FnDef(def, args) = ty else { unreachable!() };
- let result = Instance::resolve(def, &args);
- assert!(result.is_ok());
+ let instance = Instance::resolve(def, &args).unwrap();
+ let mangled_name = instance.mangled_name();
+ assert!(instance.has_body() || (mangled_name == "setpwent"), "Failed: {func:?}");
+ assert!(instance.has_body() ^ instance.is_foreign_item());
+ if instance.has_body() {
+ let body = instance.body().unwrap();
+ assert!(!body.locals().is_empty(), "Body must at least have a return local");
+ }
}
Goto { .. } | Assert { .. } | SwitchInt { .. } | Return | Drop { .. } => {
/* Do nothing */
@@ -105,10 +112,16 @@ fn generate_input(path: &str) -> std::io::Result<()> {
LEN > 0 && a[0]
}}
+ extern "C" {{
+ // Body should not be available.
+ fn setpwent();
+ }}
+
pub fn monomorphic() {{
let v = vec![10];
let dup = ty_param(&v);
assert_eq!(v, dup);
+ unsafe {{ setpwent() }};
}}
pub mod foo {{
diff --git a/tests/ui-fulldeps/stable-mir/check_item_kind.rs b/tests/ui-fulldeps/stable-mir/check_item_kind.rs
new file mode 100644
index 000000000..72e0e09e6
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/check_item_kind.rs
@@ -0,0 +1,84 @@
+// run-pass
+//! Test that item kind works as expected.
+
+// 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_middle;
+#[macro_use]
+extern crate rustc_smir;
+extern crate rustc_driver;
+extern crate rustc_interface;
+extern crate stable_mir;
+
+use rustc_middle::ty::TyCtxt;
+use rustc_smir::rustc_internal;
+use stable_mir::*;
+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_item_kind(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
+ let items = stable_mir::all_local_items();
+ assert_eq!(items.len(), 4);
+ // Constructor item.
+ for item in items {
+ let expected_kind = match item.name().as_str() {
+ "Dummy" => ItemKind::Ctor(CtorKind::Fn),
+ "dummy" => ItemKind::Fn,
+ "unit" => ItemKind::Fn,
+ "DUMMY_CONST" => ItemKind::Const,
+ name => unreachable!("Unexpected item {name}"),
+ };
+ assert_eq!(item.kind(), expected_kind, "Mismatched type for {}", item.name());
+ }
+ ControlFlow::Continue(())
+}
+
+/// 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 = "item_kind_input.rs";
+ generate_input(&path).unwrap();
+ let args = vec![
+ "rustc".to_string(),
+ "-Cpanic=abort".to_string(),
+ "--crate-type=lib".to_string(),
+ "--crate-name".to_string(),
+ CRATE_NAME.to_string(),
+ path.to_string(),
+ ];
+ run!(args, tcx, test_item_kind(tcx)).unwrap();
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+ let mut file = std::fs::File::create(path)?;
+ write!(
+ file,
+ r#"
+ pub struct Dummy(u32);
+ pub const DUMMY_CONST: Dummy = Dummy(0);
+ pub struct DummyUnit;
+
+ pub fn dummy() -> Dummy {{
+ Dummy(5)
+ }}
+
+ pub fn unit() -> DummyUnit {{
+ DummyUnit
+ }}
+ "#
+ )?;
+ Ok(())
+}
diff --git a/tests/ui-fulldeps/stable-mir/check_ty_fold.rs b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs
new file mode 100644
index 000000000..b90d47d45
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs
@@ -0,0 +1,115 @@
+// run-pass
+//! Test that users are able to use stable mir APIs to retrieve monomorphized types, and that
+//! we have an error handling for trying to instantiate types with incorrect arguments.
+
+// 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_middle;
+#[macro_use]
+extern crate rustc_smir;
+extern crate rustc_driver;
+extern crate rustc_interface;
+extern crate stable_mir;
+
+use rustc_middle::ty::TyCtxt;
+use rustc_smir::rustc_internal;
+use stable_mir::ty::{RigidTy, TyKind, Ty, };
+use stable_mir::mir::{Body, MirVisitor, FieldIdx, Place, ProjectionElem, visit::{Location,
+ PlaceContext}};
+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(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
+ let main_fn = stable_mir::entry_fn();
+ let body = main_fn.unwrap().body();
+ let mut visitor = PlaceVisitor{ body: &body, tested: false};
+ visitor.visit_body(&body);
+ assert!(visitor.tested);
+ ControlFlow::Continue(())
+}
+
+struct PlaceVisitor<'a> {
+ body: &'a Body,
+ /// Used to ensure that the test was reachable. Otherwise this test would vacuously succeed.
+ tested: bool,
+}
+
+/// Check that `wrapper.inner` place projection can be correctly interpreted.
+/// Ensure that instantiation is correct.
+fn check_tys(local_ty: Ty, idx: FieldIdx, expected_ty: Ty) {
+ let TyKind::RigidTy(RigidTy::Adt(def, args)) = local_ty.kind() else { unreachable!() };
+ assert_eq!(def.ty_with_args(&args), local_ty);
+
+ let field_def = &def.variants_iter().next().unwrap().fields()[idx];
+ let field_ty = field_def.ty_with_args(&args);
+ assert_eq!(field_ty, expected_ty);
+
+ // Check that the generic version is different than the instantiated one.
+ let field_ty_gen = field_def.ty();
+ assert_ne!(field_ty_gen, field_ty);
+}
+
+impl<'a> MirVisitor for PlaceVisitor<'a> {
+ fn visit_place(&mut self, place: &Place, _ptx: PlaceContext, _loc: Location) {
+ let start_ty = self.body.locals()[place.local].ty;
+ match place.projection.as_slice() {
+ [ProjectionElem::Field(idx, ty)] => {
+ check_tys(start_ty, *idx, *ty);
+ self.tested = true;
+ }
+ _ => {}
+ }
+ }
+}
+
+/// 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 = "ty_fold_input.rs";
+ generate_input(&path).unwrap();
+ let args = vec![
+ "rustc".to_string(),
+ "-Cpanic=abort".to_string(),
+ "--crate-name".to_string(),
+ CRATE_NAME.to_string(),
+ path.to_string(),
+ ];
+ run!(args, tcx, test_stable_mir(tcx)).unwrap();
+}
+
+fn generate_input(path: &str) -> std::io::Result<()> {
+ let mut file = std::fs::File::create(path)?;
+ write!(
+ file,
+ r#"
+ struct Wrapper<T: Default> {{
+ pub inner: T
+ }}
+
+ impl<T: Default> Wrapper<T> {{
+ pub fn new() -> Wrapper<T> {{
+ Wrapper {{ inner: T::default() }}
+ }}
+ }}
+
+ fn main() {{
+ let wrapper = Wrapper::<u8>::new();
+ let _inner = wrapper.inner;
+ }}
+ "#
+ )?;
+ Ok(())
+}
diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs
index ed6b786f5..c2035430a 100644
--- a/tests/ui-fulldeps/stable-mir/crate-info.rs
+++ b/tests/ui-fulldeps/stable-mir/crate-info.rs
@@ -22,6 +22,8 @@ extern crate stable_mir;
use rustc_hir::def::DefKind;
use rustc_middle::ty::TyCtxt;
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;
@@ -120,13 +122,14 @@ fn test_stable_mir(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
let monomorphic = get_item(&items, (DefKind::Fn, "monomorphic")).unwrap();
let instance = Instance::try_from(monomorphic.clone()).unwrap();
- for block in instance.body().blocks {
+ 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()).kind() else { unreachable!() };
+ 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().locals()[1].ty.kind() {
+ match next_func.body().unwrap().locals()[1].ty.kind() {
TyKind::RigidTy(RigidTy::Uint(_)) | TyKind::RigidTy(RigidTy::Tuple(_)) => {}
other => panic!("{other:?}"),
}
@@ -172,7 +175,8 @@ fn get_item<'a>(
item: (DefKind, &str),
) -> Option<&'a stable_mir::CrateItem> {
items.iter().find(|crate_item| {
- crate_item.kind().to_string() == format!("{:?}", item.0) && crate_item.name() == item.1
+ matches!((item.0, crate_item.kind()), (DefKind::Fn, ItemKind::Fn) | (DefKind::Const,
+ ItemKind::Const)) && crate_item.name() == item.1
})
}
diff --git a/tests/ui-fulldeps/stable-mir/projections.rs b/tests/ui-fulldeps/stable-mir/projections.rs
new file mode 100644
index 000000000..8c3fda7b6
--- /dev/null
+++ b/tests/ui-fulldeps/stable-mir/projections.rs
@@ -0,0 +1,180 @@
+// 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)]
+#![feature(control_flow_enum)]
+
+extern crate rustc_hir;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_smir;
+extern crate rustc_driver;
+extern crate rustc_interface;
+extern crate stable_mir;
+
+use rustc_middle::ty::TyCtxt;
+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(_tcx: TyCtxt<'_>) -> 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, tcx, test_place_projections(tcx)).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(())
+}
diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs
index de5148bb5..027b0e7d9 100644
--- a/tests/ui-fulldeps/stable-mir/smir_visitor.rs
+++ b/tests/ui-fulldeps/stable-mir/smir_visitor.rs
@@ -40,7 +40,7 @@ fn test_visitor(_tcx: TyCtxt<'_>) -> ControlFlow<()> {
let exit_fn = main_visitor.calls.last().unwrap();
assert!(exit_fn.mangled_name().contains("exit_fn"), "Unexpected last function: {exit_fn:?}");
- let exit_body = exit_fn.body();
+ let exit_body = exit_fn.body().unwrap();
let exit_visitor = TestVisitor::collect(&exit_body);
assert!(exit_visitor.ret_val.is_some());
assert_eq!(exit_visitor.args.len(), 1);
@@ -92,7 +92,8 @@ impl<'a> mir::MirVisitor for TestVisitor<'a> {
fn visit_terminator(&mut self, term: &mir::Terminator, location: mir::visit::Location) {
if let mir::TerminatorKind::Call { func, .. } = &term.kind {
- let ty::TyKind::RigidTy(ty) = func.ty(self.body.locals()).kind() else { unreachable!
+ let ty::TyKind::RigidTy(ty) = func.ty(self.body.locals()).unwrap().kind() else {
+ unreachable!
() };
let ty::RigidTy::FnDef(def, args) = ty else { unreachable!() };
self.calls.push(mir::mono::Instance::resolve(def, &args).unwrap());