From 7bca1f2675d9f63b09ed9641e9aefe05b8ccc675 Mon Sep 17 00:00:00 2001 From: Sky Date: Tue, 11 Feb 2025 11:49:14 -0500 Subject: Fix missing const for inherent pointer `replace` methods --- library/core/src/ptr/mut_ptr.rs | 3 ++- library/core/src/ptr/non_null.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index d1b0104c0fa..c102e21026c 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1577,8 +1577,9 @@ impl *mut T { /// /// [`ptr::replace`]: crate::ptr::replace() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_stable(feature = "const_inherent_ptr_replace", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] - pub unsafe fn replace(self, src: T) -> T + pub const unsafe fn replace(self, src: T) -> T where T: Sized, { diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index d93069d384e..4c7f0817cf3 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1169,7 +1169,8 @@ impl NonNull { /// [`ptr::replace`]: crate::ptr::replace() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - pub unsafe fn replace(self, src: T) -> T + #[rustc_const_stable(feature = "const_inherent_ptr_replace", since = "CURRENT_RUSTC_VERSION")] + pub const unsafe fn replace(self, src: T) -> T where T: Sized, { -- cgit 1.4.1-3-g733a5 From 9b889e919807c7f5c377c9c6b17d55276c8e3351 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Sat, 29 Mar 2025 14:43:32 -0700 Subject: Rename internal module from statik to no_threads This module is named in reference to the keyword, but the term is somewhat overloaded. Rename it to more clearly describe it and avoid the misspelling. --- library/std/src/sys/thread_local/mod.rs | 6 +- library/std/src/sys/thread_local/no_threads.rs | 125 +++++++++++++++++++++++++ library/std/src/sys/thread_local/statik.rs | 125 ------------------------- 3 files changed, 128 insertions(+), 128 deletions(-) create mode 100644 library/std/src/sys/thread_local/no_threads.rs delete mode 100644 library/std/src/sys/thread_local/statik.rs diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 1ff13154b7b..fb7cb40625d 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -30,9 +30,9 @@ cfg_if::cfg_if! { target_os = "zkvm", target_os = "trusty", ))] { - mod statik; - pub use statik::{EagerStorage, LazyStorage, thread_local_inner}; - pub(crate) use statik::{LocalPointer, local_pointer}; + mod no_threads; + pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner}; + pub(crate) use no_threads::{LocalPointer, local_pointer}; } else if #[cfg(target_thread_local)] { mod native; pub use native::{EagerStorage, LazyStorage, thread_local_inner}; diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs new file mode 100644 index 00000000000..4da01a84acf --- /dev/null +++ b/library/std/src/sys/thread_local/no_threads.rs @@ -0,0 +1,125 @@ +//! On some targets like wasm there's no threads, so no need to generate +//! thread locals and we can instead just use plain statics! + +use crate::cell::{Cell, UnsafeCell}; +use crate::ptr; + +#[doc(hidden)] +#[allow_internal_unstable(thread_local_internals)] +#[allow_internal_unsafe] +#[unstable(feature = "thread_local_internals", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +pub macro thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + const __INIT: $t = $init; + + // NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed. + unsafe { + $crate::thread::LocalKey::new(|_| { + static VAL: $crate::thread::local_impl::EagerStorage<$t> = + $crate::thread::local_impl::EagerStorage { value: __INIT }; + &VAL.value + }) + } + }}, + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => {{ + #[inline] + fn __init() -> $t { $init } + + unsafe { + use $crate::thread::LocalKey; + use $crate::thread::local_impl::LazyStorage; + + LocalKey::new(|init| { + static VAL: LazyStorage<$t> = LazyStorage::new(); + VAL.get(init, __init) + }) + } + }}, + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + }, +} + +#[allow(missing_debug_implementations)] +pub struct EagerStorage { + pub value: T, +} + +// SAFETY: the target doesn't have threads. +unsafe impl Sync for EagerStorage {} + +#[allow(missing_debug_implementations)] +pub struct LazyStorage { + value: UnsafeCell>, +} + +impl LazyStorage { + pub const fn new() -> LazyStorage { + LazyStorage { value: UnsafeCell::new(None) } + } + + /// Gets a pointer to the TLS value, potentially initializing it with the + /// provided parameters. + /// + /// The resulting pointer may not be used after reentrant inialialization + /// has occurred. + #[inline] + pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { + let value = unsafe { &*self.value.get() }; + match value { + Some(v) => v, + None => self.initialize(i, f), + } + } + + #[cold] + fn initialize(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { + let value = i.and_then(Option::take).unwrap_or_else(f); + // Destroy the old value, after updating the TLS variable as the + // destructor might reference it. + // FIXME(#110897): maybe panic on recursive initialization. + unsafe { + self.value.get().replace(Some(value)); + } + // SAFETY: we just set this to `Some`. + unsafe { (*self.value.get()).as_ref().unwrap_unchecked() } + } +} + +// SAFETY: the target doesn't have threads. +unsafe impl Sync for LazyStorage {} + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + p: Cell<*mut ()>, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { p: Cell::new(ptr::null_mut()) } + } + + pub fn get(&self) -> *mut () { + self.p.get() + } + + pub fn set(&self, p: *mut ()) { + self.p.set(p) + } +} + +// SAFETY: the target doesn't have threads. +unsafe impl Sync for LocalPointer {} diff --git a/library/std/src/sys/thread_local/statik.rs b/library/std/src/sys/thread_local/statik.rs deleted file mode 100644 index 4da01a84acf..00000000000 --- a/library/std/src/sys/thread_local/statik.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! On some targets like wasm there's no threads, so no need to generate -//! thread locals and we can instead just use plain statics! - -use crate::cell::{Cell, UnsafeCell}; -use crate::ptr; - -#[doc(hidden)] -#[allow_internal_unstable(thread_local_internals)] -#[allow_internal_unsafe] -#[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] -pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ - const __INIT: $t = $init; - - // NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed. - unsafe { - $crate::thread::LocalKey::new(|_| { - static VAL: $crate::thread::local_impl::EagerStorage<$t> = - $crate::thread::local_impl::EagerStorage { value: __INIT }; - &VAL.value - }) - } - }}, - - // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => {{ - #[inline] - fn __init() -> $t { $init } - - unsafe { - use $crate::thread::LocalKey; - use $crate::thread::local_impl::LazyStorage; - - LocalKey::new(|init| { - static VAL: LazyStorage<$t> = LazyStorage::new(); - VAL.get(init, __init) - }) - } - }}, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); - }, -} - -#[allow(missing_debug_implementations)] -pub struct EagerStorage { - pub value: T, -} - -// SAFETY: the target doesn't have threads. -unsafe impl Sync for EagerStorage {} - -#[allow(missing_debug_implementations)] -pub struct LazyStorage { - value: UnsafeCell>, -} - -impl LazyStorage { - pub const fn new() -> LazyStorage { - LazyStorage { value: UnsafeCell::new(None) } - } - - /// Gets a pointer to the TLS value, potentially initializing it with the - /// provided parameters. - /// - /// The resulting pointer may not be used after reentrant inialialization - /// has occurred. - #[inline] - pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { - let value = unsafe { &*self.value.get() }; - match value { - Some(v) => v, - None => self.initialize(i, f), - } - } - - #[cold] - fn initialize(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { - let value = i.and_then(Option::take).unwrap_or_else(f); - // Destroy the old value, after updating the TLS variable as the - // destructor might reference it. - // FIXME(#110897): maybe panic on recursive initialization. - unsafe { - self.value.get().replace(Some(value)); - } - // SAFETY: we just set this to `Some`. - unsafe { (*self.value.get()).as_ref().unwrap_unchecked() } - } -} - -// SAFETY: the target doesn't have threads. -unsafe impl Sync for LazyStorage {} - -#[rustc_macro_transparency = "semitransparent"] -pub(crate) macro local_pointer { - () => {}, - ($vis:vis static $name:ident; $($rest:tt)*) => { - $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); - $crate::sys::thread_local::local_pointer! { $($rest)* } - }, -} - -pub(crate) struct LocalPointer { - p: Cell<*mut ()>, -} - -impl LocalPointer { - pub const fn __new() -> LocalPointer { - LocalPointer { p: Cell::new(ptr::null_mut()) } - } - - pub fn get(&self) -> *mut () { - self.p.get() - } - - pub fn set(&self, p: *mut ()) { - self.p.set(p) - } -} - -// SAFETY: the target doesn't have threads. -unsafe impl Sync for LocalPointer {} -- cgit 1.4.1-3-g733a5 From 89d9dd6e158c18a79cd547dc172ba5739203097b Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sat, 5 Apr 2025 08:59:13 +0000 Subject: Only `format!` error message on failure --- compiler/rustc_smir/src/rustc_smir/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 52c5b425c14..0dedb55f6fc 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -27,7 +27,7 @@ pub(crate) fn new_allocation<'tcx>( tables: &mut Tables<'tcx>, ) -> Allocation { try_new_allocation(ty, const_value, tables) - .expect(&format!("Failed to convert: {const_value:?} to {ty:?}")) + .unwrap_or_else(|_| panic!("Failed to convert: {const_value:?} to {ty:?}")) } #[allow(rustc::usage_of_qualified_ty)] -- cgit 1.4.1-3-g733a5 From 33d62dd208bcb9049a128bbc241508e7e12c96c9 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sat, 5 Apr 2025 08:59:49 +0000 Subject: Dedup call to layout query --- compiler/rustc_smir/src/rustc_smir/alloc.rs | 36 +++++++++-------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 0dedb55f6fc..26dfd2fbe44 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -36,39 +36,30 @@ pub(crate) fn try_new_allocation<'tcx>( const_value: ConstValue<'tcx>, tables: &mut Tables<'tcx>, ) -> Result { + let layout = tables + .tcx + .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) + .map_err(|e| e.stable(tables))?; Ok(match const_value { ConstValue::Scalar(scalar) => { let size = scalar.size(); - let align = tables - .tcx - .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) - .map_err(|e| e.stable(tables))? - .align; - let mut allocation = - rustc_middle::mir::interpret::Allocation::new(size, align.abi, AllocInit::Uninit); + let mut allocation = rustc_middle::mir::interpret::Allocation::new( + size, + layout.align.abi, + AllocInit::Uninit, + ); allocation .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar) .map_err(|e| e.stable(tables))?; allocation.stable(tables) } - ConstValue::ZeroSized => { - let align = tables - .tcx - .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) - .map_err(|e| e.stable(tables))? - .align; - new_empty_allocation(align.abi) - } + ConstValue::ZeroSized => new_empty_allocation(layout.align.abi), ConstValue::Slice { data, meta } => { let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data); let ptr = Pointer::new(alloc_id.into(), Size::ZERO); let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx); let scalar_meta = rustc_middle::mir::interpret::Scalar::from_target_usize(meta, &tables.tcx); - let layout = tables - .tcx - .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) - .map_err(|e| e.stable(tables))?; let mut allocation = rustc_middle::mir::interpret::Allocation::new( layout.size, layout.align.abi, @@ -92,12 +83,7 @@ pub(crate) fn try_new_allocation<'tcx>( } ConstValue::Indirect { alloc_id, offset } => { let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory(); - let ty_size = tables - .tcx - .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) - .map_err(|e| e.stable(tables))? - .size; - allocation_filter(&alloc.0, alloc_range(offset, ty_size), tables) + allocation_filter(&alloc.0, alloc_range(offset, layout.size), tables) } }) } -- cgit 1.4.1-3-g733a5 From 707d356d004029a7e5300ce51e5aed330aa4a1a7 Mon Sep 17 00:00:00 2001 From: Makai Date: Sat, 5 Apr 2025 18:23:07 +0800 Subject: let `rustc_smir` host `stable_mir` for refactoring --- Cargo.lock | 5 +- compiler/rustc_smir/Cargo.toml | 2 +- compiler/rustc_smir/src/lib.rs | 2 + compiler/rustc_smir/src/rustc_internal/internal.rs | 1 + compiler/rustc_smir/src/rustc_internal/mod.rs | 1 + compiler/rustc_smir/src/rustc_internal/pretty.rs | 1 + compiler/rustc_smir/src/rustc_smir/alloc.rs | 1 + compiler/rustc_smir/src/rustc_smir/builder.rs | 1 + compiler/rustc_smir/src/rustc_smir/context.rs | 1 + compiler/rustc_smir/src/rustc_smir/convert/abi.rs | 1 + .../rustc_smir/src/rustc_smir/convert/error.rs | 1 + compiler/rustc_smir/src/rustc_smir/convert/mir.rs | 1 + compiler/rustc_smir/src/rustc_smir/convert/mod.rs | 1 + compiler/rustc_smir/src/rustc_smir/convert/ty.rs | 1 + compiler/rustc_smir/src/rustc_smir/mod.rs | 1 + compiler/rustc_smir/src/stable_mir/abi.rs | 457 ++++++ .../src/stable_mir/compiler_interface.rs | 288 ++++ compiler/rustc_smir/src/stable_mir/crate_def.rs | 175 +++ compiler/rustc_smir/src/stable_mir/error.rs | 85 + compiler/rustc_smir/src/stable_mir/mir.rs | 8 + compiler/rustc_smir/src/stable_mir/mir/alloc.rs | 88 ++ compiler/rustc_smir/src/stable_mir/mir/body.rs | 1109 +++++++++++++ compiler/rustc_smir/src/stable_mir/mir/mono.rs | 308 ++++ compiler/rustc_smir/src/stable_mir/mir/pretty.rs | 463 ++++++ compiler/rustc_smir/src/stable_mir/mir/visit.rs | 588 +++++++ compiler/rustc_smir/src/stable_mir/mod.rs | 239 +++ compiler/rustc_smir/src/stable_mir/target.rs | 61 + compiler/rustc_smir/src/stable_mir/ty.rs | 1623 ++++++++++++++++++++ compiler/rustc_smir/src/stable_mir/visitor.rs | 226 +++ compiler/stable_mir/Cargo.toml | 3 +- compiler/stable_mir/src/abi.rs | 456 ------ compiler/stable_mir/src/compiler_interface.rs | 286 ---- compiler/stable_mir/src/crate_def.rs | 174 --- compiler/stable_mir/src/error.rs | 85 - compiler/stable_mir/src/lib.rs | 234 +-- compiler/stable_mir/src/mir.rs | 8 - compiler/stable_mir/src/mir/alloc.rs | 87 -- compiler/stable_mir/src/mir/body.rs | 1108 ------------- compiler/stable_mir/src/mir/mono.rs | 307 ---- compiler/stable_mir/src/mir/pretty.rs | 462 ------ compiler/stable_mir/src/mir/visit.rs | 586 ------- compiler/stable_mir/src/target.rs | 60 - compiler/stable_mir/src/ty.rs | 1622 ------------------- compiler/stable_mir/src/visitor.rs | 224 --- 44 files changed, 5741 insertions(+), 5700 deletions(-) create mode 100644 compiler/rustc_smir/src/stable_mir/abi.rs create mode 100644 compiler/rustc_smir/src/stable_mir/compiler_interface.rs create mode 100644 compiler/rustc_smir/src/stable_mir/crate_def.rs create mode 100644 compiler/rustc_smir/src/stable_mir/error.rs create mode 100644 compiler/rustc_smir/src/stable_mir/mir.rs create mode 100644 compiler/rustc_smir/src/stable_mir/mir/alloc.rs create mode 100644 compiler/rustc_smir/src/stable_mir/mir/body.rs create mode 100644 compiler/rustc_smir/src/stable_mir/mir/mono.rs create mode 100644 compiler/rustc_smir/src/stable_mir/mir/pretty.rs create mode 100644 compiler/rustc_smir/src/stable_mir/mir/visit.rs create mode 100644 compiler/rustc_smir/src/stable_mir/mod.rs create mode 100644 compiler/rustc_smir/src/stable_mir/target.rs create mode 100644 compiler/rustc_smir/src/stable_mir/ty.rs create mode 100644 compiler/rustc_smir/src/stable_mir/visitor.rs delete mode 100644 compiler/stable_mir/src/abi.rs delete mode 100644 compiler/stable_mir/src/compiler_interface.rs delete mode 100644 compiler/stable_mir/src/crate_def.rs delete mode 100644 compiler/stable_mir/src/error.rs delete mode 100644 compiler/stable_mir/src/mir.rs delete mode 100644 compiler/stable_mir/src/mir/alloc.rs delete mode 100644 compiler/stable_mir/src/mir/body.rs delete mode 100644 compiler/stable_mir/src/mir/mono.rs delete mode 100644 compiler/stable_mir/src/mir/pretty.rs delete mode 100644 compiler/stable_mir/src/mir/visit.rs delete mode 100644 compiler/stable_mir/src/target.rs delete mode 100644 compiler/stable_mir/src/ty.rs delete mode 100644 compiler/stable_mir/src/visitor.rs diff --git a/Cargo.lock b/Cargo.lock index 96526f7e9e7..99bd1bff020 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4432,7 +4432,7 @@ dependencies = [ "rustc_span", "rustc_target", "scoped-tls", - "stable_mir", + "serde", "tracing", ] @@ -4990,8 +4990,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "stable_mir" version = "0.1.0-preview" dependencies = [ - "scoped-tls", - "serde", + "rustc_smir", ] [[package]] diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index b2149a03a8e..fc9f411ac3c 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -14,6 +14,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } scoped-tls = "1.0" -stable_mir = {path = "../stable_mir" } +serde = { version = "1.0.125", features = [ "derive" ] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index eaba14bbf30..5d465bca4ee 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -22,3 +22,5 @@ pub mod rustc_internal; // Make this module private for now since external users should not call these directly. mod rustc_smir; + +pub mod stable_mir; diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index bb2b2dea2f3..36b68cc1398 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -21,6 +21,7 @@ use stable_mir::{CrateItem, CrateNum, DefId}; use super::RustcInternal; use crate::rustc_smir::Tables; +use crate::stable_mir; impl RustcInternal for CrateItem { type T<'tcx> = rustc_span::def_id::DefId; diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 30e9b2e72f7..a546a44c870 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -22,6 +22,7 @@ use stable_mir::ty::IndexedVal; use crate::rustc_smir::context::TablesWrapper; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; mod internal; pub mod pretty; diff --git a/compiler/rustc_smir/src/rustc_internal/pretty.rs b/compiler/rustc_smir/src/rustc_internal/pretty.rs index b752ad71ecc..0710c18746a 100644 --- a/compiler/rustc_smir/src/rustc_internal/pretty.rs +++ b/compiler/rustc_smir/src/rustc_internal/pretty.rs @@ -3,6 +3,7 @@ use std::io; use rustc_middle::ty::TyCtxt; use super::run; +use crate::stable_mir; pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> { writeln!( diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 52c5b425c14..21213857662 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -6,6 +6,7 @@ use stable_mir::mir::Mutability; use stable_mir::ty::{Allocation, ProvenanceMap}; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; /// Creates new empty `Allocation` from given `Align`. fn new_empty_allocation(align: Align) -> Allocation { diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 2eb0cea0e85..64763b71d30 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -10,6 +10,7 @@ use rustc_middle::mir::visit::MutVisitor; use rustc_middle::ty::{self, TyCtxt}; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; /// Builds a monomorphic body for a given instance. pub(crate) struct BodyBuilder<'tcx> { diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 322e86147c1..240f6f7fe45 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -35,6 +35,7 @@ use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, I use crate::rustc_internal::RustcInternal; use crate::rustc_smir::builder::BodyBuilder; use crate::rustc_smir::{Stable, Tables, alloc, filter_def_ids, new_item_kind, smir_crate}; +use crate::stable_mir; impl<'tcx> Context for TablesWrapper<'tcx> { fn target_info(&self) -> MachineInfo { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs index 62cbab9b723..7ccc785a400 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs @@ -14,6 +14,7 @@ use stable_mir::target::MachineSize as Size; use stable_mir::ty::{Align, IndexedVal, VariantIdx}; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; impl<'tcx> Stable<'tcx> for rustc_abi::VariantIdx { type T = VariantIdx; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/error.rs b/compiler/rustc_smir/src/rustc_smir/convert/error.rs index 82ecfa630dd..2cde5542483 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/error.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/error.rs @@ -6,6 +6,7 @@ use rustc_middle::mir::interpret::AllocError; use rustc_middle::ty::layout::LayoutError; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; impl<'tcx> Stable<'tcx> for LayoutError<'tcx> { type T = stable_mir::Error; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index bdd6e16a7c1..61b769bce08 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -9,6 +9,7 @@ use stable_mir::ty::{Allocation, ConstantKind, MirConst}; use stable_mir::{Error, opaque}; use crate::rustc_smir::{Stable, Tables, alloc}; +use crate::stable_mir; impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { type T = stable_mir::mir::Body; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs index a3da563af50..3494de62d83 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs @@ -3,6 +3,7 @@ use rustc_abi::FieldIdx; use crate::rustc_smir::{Stable, Tables}; +use crate::stable_mir; mod abi; mod error; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 8309809d7b5..1ba25aa0e97 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -7,6 +7,7 @@ use stable_mir::ty::{ }; use crate::rustc_smir::{Stable, Tables, alloc}; +use crate::stable_mir; impl<'tcx> Stable<'tcx> for ty::AliasTyKind { type T = stable_mir::ty::AliasKind; diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index c5d33f090a0..dea8f54a670 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -21,6 +21,7 @@ use stable_mir::{CtorKind, ItemKind}; use tracing::debug; use crate::rustc_internal::IndexMap; +use crate::stable_mir; mod alloc; mod builder; diff --git a/compiler/rustc_smir/src/stable_mir/abi.rs b/compiler/rustc_smir/src/stable_mir/abi.rs new file mode 100644 index 00000000000..3842cb7e653 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/abi.rs @@ -0,0 +1,457 @@ +use std::fmt::{self, Debug}; +use std::num::NonZero; +use std::ops::RangeInclusive; + +use serde::Serialize; +use stable_mir::compiler_interface::with; +use stable_mir::mir::FieldIdx; +use stable_mir::target::{MachineInfo, MachineSize as Size}; +use stable_mir::ty::{Align, IndexedVal, Ty, VariantIdx}; +use stable_mir::{Error, Opaque, error}; + +use crate::stable_mir; + +/// A function ABI definition. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct FnAbi { + /// The types of each argument. + pub args: Vec, + + /// The expected return type. + pub ret: ArgAbi, + + /// The count of non-variadic arguments. + /// + /// Should only be different from `args.len()` when a function is a C variadic function. + pub fixed_count: u32, + + /// The ABI convention. + pub conv: CallConvention, + + /// Whether this is a variadic C function, + pub c_variadic: bool, +} + +/// Information about the ABI of a function's argument, or return value. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct ArgAbi { + pub ty: Ty, + pub layout: Layout, + pub mode: PassMode, +} + +/// How a function argument should be passed in to the target function. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum PassMode { + /// Ignore the argument. + /// + /// The argument is either uninhabited or a ZST. + Ignore, + /// Pass the argument directly. + /// + /// The argument has a layout abi of `Scalar` or `Vector`. + Direct(Opaque), + /// Pass a pair's elements directly in two arguments. + /// + /// The argument has a layout abi of `ScalarPair`. + Pair(Opaque, Opaque), + /// Pass the argument after casting it. + Cast { pad_i32: bool, cast: Opaque }, + /// Pass the argument indirectly via a hidden pointer. + Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool }, +} + +/// The layout of a type, alongside the type itself. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct TyAndLayout { + pub ty: Ty, + pub layout: Layout, +} + +/// The layout of a type in memory. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct LayoutShape { + /// The fields location within the layout + pub fields: FieldsShape, + + /// Encodes information about multi-variant layouts. + /// Even with `Multiple` variants, a layout still has its own fields! Those are then + /// shared between all variants. + /// + /// To access all fields of this layout, both `fields` and the fields of the active variant + /// must be taken into account. + pub variants: VariantsShape, + + /// The `abi` defines how this data is passed between functions. + pub abi: ValueAbi, + + /// The ABI mandated alignment in bytes. + pub abi_align: Align, + + /// The size of this layout in bytes. + pub size: Size, +} + +impl LayoutShape { + /// Returns `true` if the layout corresponds to an unsized type. + #[inline] + pub fn is_unsized(&self) -> bool { + self.abi.is_unsized() + } + + #[inline] + pub fn is_sized(&self) -> bool { + !self.abi.is_unsized() + } + + /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1). + pub fn is_1zst(&self) -> bool { + self.is_sized() && self.size.bits() == 0 && self.abi_align == 1 + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct Layout(usize); + +impl Layout { + pub fn shape(self) -> LayoutShape { + with(|cx| cx.layout_shape(self)) + } +} + +impl IndexedVal for Layout { + fn to_val(index: usize) -> Self { + Layout(index) + } + fn to_index(&self) -> usize { + self.0 + } +} + +/// Describes how the fields of a type are shaped in memory. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum FieldsShape { + /// Scalar primitives and `!`, which never have fields. + Primitive, + + /// All fields start at no offset. The `usize` is the field count. + Union(NonZero), + + /// Array/vector-like placement, with all fields of identical types. + Array { stride: Size, count: u64 }, + + /// Struct-like placement, with precomputed offsets. + /// + /// Fields are guaranteed to not overlap, but note that gaps + /// before, between and after all the fields are NOT always + /// padding, and as such their contents may not be discarded. + /// For example, enum variants leave a gap at the start, + /// where the discriminant field in the enum layout goes. + Arbitrary { + /// Offsets for the first byte of each field, + /// ordered to match the source definition order. + /// I.e.: It follows the same order as [super::ty::VariantDef::fields()]. + /// This vector does not go in increasing order. + offsets: Vec, + }, +} + +impl FieldsShape { + pub fn fields_by_offset_order(&self) -> Vec { + match self { + FieldsShape::Primitive => vec![], + FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(), + FieldsShape::Arbitrary { offsets, .. } => { + let mut indices = (0..offsets.len()).collect::>(); + indices.sort_by_key(|idx| offsets[*idx]); + indices + } + } + } + + pub fn count(&self) -> usize { + match self { + FieldsShape::Primitive => 0, + FieldsShape::Union(count) => count.get(), + FieldsShape::Array { count, .. } => *count as usize, + FieldsShape::Arbitrary { offsets, .. } => offsets.len(), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum VariantsShape { + /// A type with no valid variants. Must be uninhabited. + Empty, + + /// Single enum variants, structs/tuples, unions, and all non-ADTs. + Single { index: VariantIdx }, + + /// Enum-likes with more than one inhabited variant: each variant comes with + /// a *discriminant* (usually the same as the variant index but the user can + /// assign explicit discriminant values). That discriminant is encoded + /// as a *tag* on the machine. The layout of each variant is + /// a struct, and they all have space reserved for the tag. + /// For enums, the tag is the sole field of the layout. + Multiple { + tag: Scalar, + tag_encoding: TagEncoding, + tag_field: usize, + variants: Vec, + }, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum TagEncoding { + /// The tag directly stores the discriminant, but possibly with a smaller layout + /// (so converting the tag to the discriminant can require sign extension). + Direct, + + /// Niche (values invalid for a type) encoding the discriminant: + /// Discriminant and variant index coincide. + /// The variant `untagged_variant` contains a niche at an arbitrary + /// offset (field `tag_field` of the enum), which for a variant with + /// discriminant `d` is set to + /// `(d - niche_variants.start).wrapping_add(niche_start)`. + /// + /// For example, `Option<(usize, &T)>` is represented such that + /// `None` has a null pointer for the second tuple field, and + /// `Some` is the identity function (with a non-null reference). + Niche { + untagged_variant: VariantIdx, + niche_variants: RangeInclusive, + niche_start: u128, + }, +} + +/// Describes how values of the type are passed by target ABIs, +/// in terms of categories of C types there are ABI rules for. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum ValueAbi { + Scalar(Scalar), + ScalarPair(Scalar, Scalar), + Vector { + element: Scalar, + count: u64, + }, + Aggregate { + /// If true, the size is exact, otherwise it's only a lower bound. + sized: bool, + }, +} + +impl ValueAbi { + /// Returns `true` if the layout corresponds to an unsized type. + pub fn is_unsized(&self) -> bool { + match *self { + ValueAbi::Scalar(_) | ValueAbi::ScalarPair(..) | ValueAbi::Vector { .. } => false, + ValueAbi::Aggregate { sized } => !sized, + } + } +} + +/// Information about one scalar component of a Rust type. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)] +pub enum Scalar { + Initialized { + /// The primitive type used to represent this value. + value: Primitive, + /// The range that represents valid values. + /// The range must be valid for the `primitive` size. + valid_range: WrappingRange, + }, + Union { + /// Unions never have niches, so there is no `valid_range`. + /// Even for unions, we need to use the correct registers for the kind of + /// values inside the union, so we keep the `Primitive` type around. + /// It is also used to compute the size of the scalar. + value: Primitive, + }, +} + +impl Scalar { + pub fn has_niche(&self, target: &MachineInfo) -> bool { + match self { + Scalar::Initialized { value, valid_range } => { + !valid_range.is_full(value.size(target)).unwrap() + } + Scalar::Union { .. } => false, + } + } +} + +/// Fundamental unit of memory access and layout. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)] +pub enum Primitive { + /// The `bool` is the signedness of the `Integer` type. + /// + /// One would think we would not care about such details this low down, + /// but some ABIs are described in terms of C types and ISAs where the + /// integer arithmetic is done on {sign,zero}-extended registers, e.g. + /// a negative integer passed by zero-extension will appear positive in + /// the callee, and most operations on it will produce the wrong values. + Int { + length: IntegerLength, + signed: bool, + }, + Float { + length: FloatLength, + }, + Pointer(AddressSpace), +} + +impl Primitive { + pub fn size(self, target: &MachineInfo) -> Size { + match self { + Primitive::Int { length, .. } => Size::from_bits(length.bits()), + Primitive::Float { length } => Size::from_bits(length.bits()), + Primitive::Pointer(_) => target.pointer_width, + } + } +} + +/// Enum representing the existing integer lengths. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] +pub enum IntegerLength { + I8, + I16, + I32, + I64, + I128, +} + +/// Enum representing the existing float lengths. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] +pub enum FloatLength { + F16, + F32, + F64, + F128, +} + +impl IntegerLength { + pub fn bits(self) -> usize { + match self { + IntegerLength::I8 => 8, + IntegerLength::I16 => 16, + IntegerLength::I32 => 32, + IntegerLength::I64 => 64, + IntegerLength::I128 => 128, + } + } +} + +impl FloatLength { + pub fn bits(self) -> usize { + match self { + FloatLength::F16 => 16, + FloatLength::F32 => 32, + FloatLength::F64 => 64, + FloatLength::F128 => 128, + } + } +} + +/// An identifier that specifies the address space that some operation +/// should operate on. Special address spaces have an effect on code generation, +/// depending on the target and the address spaces it implements. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct AddressSpace(pub u32); + +impl AddressSpace { + /// The default address space, corresponding to data space. + pub const DATA: Self = AddressSpace(0); +} + +/// Inclusive wrap-around range of valid values (bitwise representation), that is, if +/// start > end, it represents `start..=MAX`, followed by `0..=end`. +/// +/// That is, for an i8 primitive, a range of `254..=2` means following +/// sequence: +/// +/// 254 (-2), 255 (-1), 0, 1, 2 +#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] +pub struct WrappingRange { + pub start: u128, + pub end: u128, +} + +impl WrappingRange { + /// Returns `true` if `size` completely fills the range. + #[inline] + pub fn is_full(&self, size: Size) -> Result { + let Some(max_value) = size.unsigned_int_max() else { + return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits())); + }; + if self.start <= max_value && self.end <= max_value { + Ok(self.start == (self.end.wrapping_add(1) & max_value)) + } else { + Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits())) + } + } + + /// Returns `true` if `v` is contained in the range. + #[inline(always)] + pub fn contains(&self, v: u128) -> bool { + if self.wraps_around() { + self.start <= v || v <= self.end + } else { + self.start <= v && v <= self.end + } + } + + /// Returns `true` if the range wraps around. + /// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`. + /// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`. + #[inline] + pub fn wraps_around(&self) -> bool { + self.start > self.end + } +} + +impl Debug for WrappingRange { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.start > self.end { + write!(fmt, "(..={}) | ({}..)", self.end, self.start)?; + } else { + write!(fmt, "{}..={}", self.start, self.end)?; + } + Ok(()) + } +} + +/// General language calling conventions. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum CallConvention { + C, + Rust, + + Cold, + PreserveMost, + PreserveAll, + + // Target-specific calling conventions. + ArmAapcs, + CCmseNonSecureCall, + CCmseNonSecureEntry, + + Msp430Intr, + + PtxKernel, + + GpuKernel, + + X86Fastcall, + X86Intr, + X86Stdcall, + X86ThisCall, + X86VectorCall, + + X86_64SysV, + X86_64Win64, + + AvrInterrupt, + AvrNonBlockingInterrupt, + + RiscvInterrupt, +} diff --git a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs new file mode 100644 index 00000000000..33d950bb951 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs @@ -0,0 +1,288 @@ +//! Define the interface with the Rust compiler. +//! +//! StableMIR users should not use any of the items in this module directly. +//! These APIs have no stability guarantee. + +use std::cell::Cell; + +use stable_mir::abi::{FnAbi, Layout, LayoutShape}; +use stable_mir::crate_def::Attribute; +use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; +use stable_mir::mir::mono::{Instance, InstanceDef, StaticDef}; +use stable_mir::mir::{BinOp, Body, Place, UnOp}; +use stable_mir::target::MachineInfo; +use stable_mir::ty::{ + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, + ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, + ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl, + TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, +}; +use stable_mir::{ + AssocItems, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, + ItemKind, Symbol, TraitDecls, mir, +}; + +use crate::stable_mir; + +/// This trait defines the interface between stable_mir and the Rust compiler. +/// Do not use this directly. +pub trait Context { + fn entry_fn(&self) -> Option; + /// Retrieve all items of the local crate that have a MIR associated with them. + fn all_local_items(&self) -> CrateItems; + /// Retrieve the body of a function. + /// This function will panic if the body is not available. + fn mir_body(&self, item: DefId) -> mir::Body; + /// Check whether the body of a function is available. + fn has_body(&self, item: DefId) -> bool; + fn foreign_modules(&self, crate_num: CrateNum) -> Vec; + + /// Retrieve all functions defined in this crate. + fn crate_functions(&self, crate_num: CrateNum) -> Vec; + + /// Retrieve all static items defined in this crate. + fn crate_statics(&self, crate_num: CrateNum) -> Vec; + fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule; + fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec; + fn all_trait_decls(&self) -> TraitDecls; + fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls; + fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl; + fn all_trait_impls(&self) -> ImplTraitDecls; + fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls; + fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait; + fn generics_of(&self, def_id: DefId) -> Generics; + fn predicates_of(&self, def_id: DefId) -> GenericPredicates; + fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates; + /// Get information about the local crate. + fn local_crate(&self) -> Crate; + /// Retrieve a list of all external crates. + fn external_crates(&self) -> Vec; + + /// Find a crate with the given name. + fn find_crates(&self, name: &str) -> Vec; + + /// Returns the name of given `DefId` + fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; + + /// Return registered tool attributes with the given attribute name. + /// + /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool + /// attributes will simply return an empty list. + /// + /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. + fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec; + + /// Get all tool attributes of a definition. + fn all_tool_attrs(&self, def_id: DefId) -> Vec; + + /// Returns printable, human readable form of `Span` + fn span_to_string(&self, span: Span) -> String; + + /// Return filename from given `Span`, for diagnostic purposes + fn get_filename(&self, span: &Span) -> Filename; + + /// Return lines corresponding to this `Span` + fn get_lines(&self, span: &Span) -> LineInfo; + + /// Returns the `kind` of given `DefId` + fn item_kind(&self, item: CrateItem) -> ItemKind; + + /// Returns whether this is a foreign item. + fn is_foreign_item(&self, item: DefId) -> bool; + + /// Returns the kind of a given foreign item. + fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind; + + /// Returns the kind of a given algebraic data type + fn adt_kind(&self, def: AdtDef) -> AdtKind; + + /// Returns if the ADT is a box. + fn adt_is_box(&self, def: AdtDef) -> bool; + + /// Returns whether this ADT is simd. + fn adt_is_simd(&self, def: AdtDef) -> bool; + + /// Returns whether this definition is a C string. + fn adt_is_cstr(&self, def: AdtDef) -> bool; + + /// Retrieve the function signature for the given generic arguments. + fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig; + + /// Retrieve the intrinsic definition if the item corresponds one. + fn intrinsic(&self, item: DefId) -> Option; + + /// Retrieve the plain function name of an intrinsic. + fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol; + + /// Retrieve the closure signature for the given generic arguments. + fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig; + + /// The number of variants in this ADT. + fn adt_variants_len(&self, def: AdtDef) -> usize; + + /// The name of a variant. + fn variant_name(&self, def: VariantDef) -> Symbol; + fn variant_fields(&self, def: VariantDef) -> Vec; + + /// Evaluate constant as a target usize. + fn eval_target_usize(&self, cnst: &MirConst) -> Result; + fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result; + + /// Create a new zero-sized constant. + fn try_new_const_zst(&self, ty: Ty) -> Result; + + /// Create a new constant that represents the given string value. + fn new_const_str(&self, value: &str) -> MirConst; + + /// Create a new constant that represents the given boolean value. + fn new_const_bool(&self, value: bool) -> MirConst; + + /// Create a new constant that represents the given value. + fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; + fn try_new_ty_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; + + /// Create a new type from the given kind. + fn new_rigid_ty(&self, kind: RigidTy) -> Ty; + + /// Create a new box type, `Box`, for the given inner type `T`. + fn new_box_ty(&self, ty: Ty) -> Ty; + + /// Returns the type of given crate item. + fn def_ty(&self, item: DefId) -> Ty; + + /// Returns the type of given definition instantiated with the given arguments. + fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty; + + /// Returns literal value of a const as a string. + fn mir_const_pretty(&self, cnst: &MirConst) -> String; + + /// `Span` of an item + fn span_of_an_item(&self, def_id: DefId) -> Span; + + fn ty_const_pretty(&self, ct: TyConstId) -> String; + + /// Obtain the representation of a type. + fn ty_pretty(&self, ty: Ty) -> String; + + /// Obtain the representation of a type. + fn ty_kind(&self, ty: Ty) -> TyKind; + + // Get the discriminant Ty for this Ty if there's one. + fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty; + + /// Get the body of an Instance which is already monomorphized. + fn instance_body(&self, instance: InstanceDef) -> Option; + + /// Get the instance type with generic instantiations applied and lifetimes erased. + fn instance_ty(&self, instance: InstanceDef) -> Ty; + + /// Get the instantiation types. + fn instance_args(&self, def: InstanceDef) -> GenericArgs; + + /// Get the instance. + fn instance_def_id(&self, instance: InstanceDef) -> DefId; + + /// Get the instance mangled name. + fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol; + + /// Check if this is an empty DropGlue shim. + fn is_empty_drop_shim(&self, def: InstanceDef) -> bool; + + /// Check if this is an empty AsyncDropGlueCtor shim. + fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool; + + /// Convert a non-generic crate item into an instance. + /// This function will panic if the item is generic. + fn mono_instance(&self, def_id: DefId) -> Instance; + + /// Item requires monomorphization. + fn requires_monomorphization(&self, def_id: DefId) -> bool; + + /// Resolve an instance from the given function definition and generic arguments. + fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option; + + /// Resolve an instance for drop_in_place for the given type. + fn resolve_drop_in_place(&self, ty: Ty) -> Instance; + + /// Resolve instance for a function pointer. + fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option; + + /// Resolve instance for a closure with the requested type. + fn resolve_closure( + &self, + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Option; + + /// Evaluate a static's initializer. + fn eval_static_initializer(&self, def: StaticDef) -> Result; + + /// Try to evaluate an instance into a constant. + fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result; + + /// Retrieve global allocation for the given allocation ID. + fn global_alloc(&self, id: AllocId) -> GlobalAlloc; + + /// Retrieve the id for the virtual table. + fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option; + fn krate(&self, def_id: DefId) -> Crate; + fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol; + + /// Return information about the target machine. + fn target_info(&self) -> MachineInfo; + + /// Get an instance ABI. + fn instance_abi(&self, def: InstanceDef) -> Result; + + /// Get the ABI of a function pointer. + fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result; + + /// Get the layout of a type. + fn ty_layout(&self, ty: Ty) -> Result; + + /// Get the layout shape. + fn layout_shape(&self, id: Layout) -> LayoutShape; + + /// Get a debug string representation of a place. + fn place_pretty(&self, place: &Place) -> String; + + /// Get the resulting type of binary operation. + fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; + + /// Get the resulting type of unary operation. + fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty; + + /// Get all associated items of a definition. + fn associated_items(&self, def_id: DefId) -> AssocItems; +} + +// A thread local variable that stores a pointer to the tables mapping between TyCtxt +// datastructures and stable MIR datastructures +scoped_tls::scoped_thread_local!(static TLV: Cell<*const ()>); + +pub fn run(context: &dyn Context, f: F) -> Result +where + F: FnOnce() -> T, +{ + if TLV.is_set() { + Err(Error::from("StableMIR already running")) + } else { + let ptr: *const () = (&raw const context) as _; + TLV.set(&Cell::new(ptr), || Ok(f())) + } +} + +/// Execute the given function with access the compiler [Context]. +/// +/// I.e., This function will load the current context and calls a function with it. +/// Do not nest these, as that will ICE. +pub(crate) fn with(f: impl FnOnce(&dyn Context) -> R) -> R { + assert!(TLV.is_set()); + TLV.with(|tlv| { + let ptr = tlv.get(); + assert!(!ptr.is_null()); + f(unsafe { *(ptr as *const &dyn Context) }) + }) +} diff --git a/compiler/rustc_smir/src/stable_mir/crate_def.rs b/compiler/rustc_smir/src/stable_mir/crate_def.rs new file mode 100644 index 00000000000..64f7ef9b314 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/crate_def.rs @@ -0,0 +1,175 @@ +//! Module that define a common trait for things that represent a crate definition, +//! such as, a function, a trait, an enum, and any other definitions. + +use serde::Serialize; +use stable_mir::ty::{GenericArgs, Span, Ty}; +use stable_mir::{AssocItems, Crate, Symbol, with}; + +use crate::stable_mir; + +/// A unique identification number for each item accessible for the current compilation unit. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] +pub struct DefId(pub(crate) usize); + +impl DefId { + /// Return fully qualified name of this definition + pub fn name(&self) -> Symbol { + with(|cx| cx.def_name(*self, false)) + } + + /// Return a trimmed name of this definition. + /// + /// This can be used to print more user friendly diagnostic messages. + /// + /// If a symbol name can only be imported from one place for a type, and as + /// long as it was not glob-imported anywhere in the current crate, we trim its + /// path and print only the name. + /// + /// For example, this function may shorten `std::vec::Vec` to just `Vec`, + /// as long as there is no other `Vec` importable anywhere. + pub fn trimmed_name(&self) -> Symbol { + with(|cx| cx.def_name(*self, true)) + } +} + +/// A trait for retrieving information about a particular definition. +/// +/// Implementors must provide the implementation of `def_id` which will be used to retrieve +/// information about a crate's definition. +pub trait CrateDef { + /// Retrieve the unique identifier for the current definition. + fn def_id(&self) -> DefId; + + /// Return the fully qualified name of the current definition. + /// + /// See [`DefId::name`] for more details + fn name(&self) -> Symbol { + self.def_id().name() + } + + /// Return a trimmed name of this definition. + /// + /// See [`DefId::trimmed_name`] for more details + fn trimmed_name(&self) -> Symbol { + self.def_id().trimmed_name() + } + + /// Return information about the crate where this definition is declared. + /// + /// This will return the crate number and its name. + fn krate(&self) -> Crate { + let def_id = self.def_id(); + with(|cx| cx.krate(def_id)) + } + + /// Return the span of this definition. + fn span(&self) -> Span { + let def_id = self.def_id(); + with(|cx| cx.span_of_an_item(def_id)) + } + + /// Return registered tool attributes with the given attribute name. + /// + /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool + /// attributes will simply return an empty list. + /// + /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. + fn tool_attrs(&self, attr: &[Symbol]) -> Vec { + let def_id = self.def_id(); + with(|cx| cx.tool_attrs(def_id, attr)) + } + + /// Return all tool attributes of this definition. + fn all_tool_attrs(&self) -> Vec { + let def_id = self.def_id(); + with(|cx| cx.all_tool_attrs(def_id)) + } +} + +/// A trait that can be used to retrieve a definition's type. +/// +/// Note that not every CrateDef has a type `Ty`. They should not implement this trait. +pub trait CrateDefType: CrateDef { + /// Returns the type of this crate item. + fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.def_id())) + } + + /// Retrieve the type of this definition by instantiating and normalizing it with `args`. + /// + /// This will panic if instantiation fails. + fn ty_with_args(&self, args: &GenericArgs) -> Ty { + with(|cx| cx.def_ty_with_args(self.def_id(), args)) + } +} + +/// A trait for retrieving all items from a definition within a crate. +pub trait CrateDefItems: CrateDef { + /// Retrieve all associated items from a definition. + fn associated_items(&self) -> AssocItems { + with(|cx| cx.associated_items(self.def_id())) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Attribute { + value: String, + span: Span, +} + +impl Attribute { + pub fn new(value: String, span: Span) -> Attribute { + Attribute { value, span } + } + + /// Get the span of this attribute. + pub fn span(&self) -> Span { + self.span + } + + /// Get the string representation of this attribute. + pub fn as_str(&self) -> &str { + &self.value + } +} + +macro_rules! crate_def { + ( $(#[$attr:meta])* + $vis:vis $name:ident $(;)? + ) => { + $(#[$attr])* + #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] + $vis struct $name(pub DefId); + + impl CrateDef for $name { + fn def_id(&self) -> DefId { + self.0 + } + } + }; +} + +macro_rules! crate_def_with_ty { + ( $(#[$attr:meta])* + $vis:vis $name:ident $(;)? + ) => { + $(#[$attr])* + #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] + $vis struct $name(pub DefId); + + impl CrateDef for $name { + fn def_id(&self) -> DefId { + self.0 + } + } + + impl CrateDefType for $name {} + }; +} + +macro_rules! impl_crate_def_items { + ( $name:ident $(;)? ) => { + impl CrateDefItems for $name {} + }; +} diff --git a/compiler/rustc_smir/src/stable_mir/error.rs b/compiler/rustc_smir/src/stable_mir/error.rs new file mode 100644 index 00000000000..050752e41eb --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/error.rs @@ -0,0 +1,85 @@ +//! When things go wrong, we need some error handling. +//! There are a few different types of errors in StableMIR: +//! +//! - [CompilerError]: This represents errors that can be raised when invoking the compiler. +//! - [Error]: Generic error that represents the reason why a request that could not be fulfilled. + +use std::fmt::{Debug, Display, Formatter}; +use std::{fmt, io}; + +macro_rules! error { + ($fmt: literal $(,)?) => { Error(format!($fmt)) }; + ($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) }; +} + +pub(crate) use error; + +/// An error type used to represent an error that has already been reported by the compiler. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum CompilerError { + /// Compilation failed, either due to normal errors or ICE. + Failed, + /// Compilation was interrupted. + Interrupted(T), + /// Compilation skipped. This happens when users invoke rustc to retrieve information such as + /// --version. + Skipped, +} + +/// A generic error to represent an API request that cannot be fulfilled. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Error(pub(crate) String); + +impl Error { + pub fn new(msg: String) -> Self { + Self(msg) + } +} + +impl From<&str> for Error { + fn from(value: &str) -> Self { + Self(value.into()) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl Display for CompilerError +where + T: Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + CompilerError::Failed => write!(f, "Compilation Failed"), + CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason}"), + CompilerError::Skipped => write!(f, "Compilation Skipped"), + } + } +} + +impl Debug for CompilerError +where + T: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + CompilerError::Failed => write!(f, "Compilation Failed"), + CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason:?}"), + CompilerError::Skipped => write!(f, "Compilation Skipped"), + } + } +} + +impl std::error::Error for Error {} + +impl std::error::Error for CompilerError where T: Display + Debug {} + +impl From for Error { + fn from(value: io::Error) -> Self { + Error(value.to_string()) + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mir.rs b/compiler/rustc_smir/src/stable_mir/mir.rs new file mode 100644 index 00000000000..413b5152bb3 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir.rs @@ -0,0 +1,8 @@ +pub mod alloc; +mod body; +pub mod mono; +pub mod pretty; +pub mod visit; + +pub use body::*; +pub use visit::{MirVisitor, MutMirVisitor}; diff --git a/compiler/rustc_smir/src/stable_mir/mir/alloc.rs b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs new file mode 100644 index 00000000000..782f52888b7 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir/alloc.rs @@ -0,0 +1,88 @@ +//! This module provides methods to retrieve allocation information, such as static variables. + +use std::io::Read; + +use serde::Serialize; +use stable_mir::mir::mono::{Instance, StaticDef}; +use stable_mir::target::{Endian, MachineInfo}; +use stable_mir::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty}; +use stable_mir::{Error, with}; + +use crate::stable_mir; + +/// An allocation in the SMIR global memory can be either a function pointer, +/// a static, or a "real" allocation with some data in it. +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] +pub enum GlobalAlloc { + /// The alloc ID is used as a function pointer. + Function(Instance), + /// This alloc ID points to a symbolic (not-reified) vtable. + /// The `None` trait ref is used to represent auto traits. + VTable(Ty, Option>), + /// The alloc ID points to a "lazy" static variable that did not get computed (yet). + /// This is also used to break the cycle in recursive statics. + Static(StaticDef), + /// The alloc ID points to memory. + Memory(Allocation), +} + +impl From for GlobalAlloc { + fn from(value: AllocId) -> Self { + with(|cx| cx.global_alloc(value)) + } +} + +impl GlobalAlloc { + /// Retrieve the allocation id for a global allocation if it exists. + /// + /// For `[GlobalAlloc::VTable]`, this will return the allocation for the VTable of the given + /// type for the optional trait if the type implements the trait. + /// + /// This method will always return `None` for allocations other than `[GlobalAlloc::VTable]`. + pub fn vtable_allocation(&self) -> Option { + with(|cx| cx.vtable_allocation(self)) + } +} + +/// A unique identification number for each provenance +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] +pub struct AllocId(usize); + +impl IndexedVal for AllocId { + fn to_val(index: usize) -> Self { + AllocId(index) + } + fn to_index(&self) -> usize { + self.0 + } +} + +/// Utility function used to read an allocation data into a unassigned integer. +pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result { + let mut buf = [0u8; size_of::()]; + match MachineInfo::target_endianness() { + Endian::Little => { + bytes.read_exact(&mut buf[..bytes.len()])?; + Ok(u128::from_le_bytes(buf)) + } + Endian::Big => { + bytes.read_exact(&mut buf[16 - bytes.len()..])?; + Ok(u128::from_be_bytes(buf)) + } + } +} + +/// Utility function used to read an allocation data into an assigned integer. +pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result { + let mut buf = [0u8; size_of::()]; + match MachineInfo::target_endianness() { + Endian::Little => { + bytes.read_exact(&mut buf[..bytes.len()])?; + Ok(i128::from_le_bytes(buf)) + } + Endian::Big => { + bytes.read_exact(&mut buf[16 - bytes.len()..])?; + Ok(i128::from_be_bytes(buf)) + } + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs new file mode 100644 index 00000000000..b2360545424 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -0,0 +1,1109 @@ +use std::io; + +use serde::Serialize; +use stable_mir::compiler_interface::with; +use stable_mir::mir::pretty::function_body; +use stable_mir::ty::{ + AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability, + Region, RigidTy, Ty, TyConst, TyKind, VariantIdx, +}; +use stable_mir::{Error, Opaque, Span, Symbol}; + +use crate::stable_mir; + +/// The SMIR representation of a single function. +#[derive(Clone, Debug, Serialize)] +pub struct Body { + pub blocks: Vec, + + /// Declarations of locals within the function. + /// + /// The first local is the return value pointer, followed by `arg_count` + /// locals for the function arguments, followed by any user-declared + /// variables and temporaries. + pub(super) locals: LocalDecls, + + /// The number of arguments this function takes. + pub(super) arg_count: usize, + + /// Debug information pertaining to user variables, including captures. + pub var_debug_info: Vec, + + /// Mark an argument (which must be a tuple) as getting passed as its individual components. + /// + /// This is used for the "rust-call" ABI such as closures. + pub(super) spread_arg: Option, + + /// The span that covers the entire function body. + pub span: Span, +} + +pub type BasicBlockIdx = usize; + +impl Body { + /// Constructs a `Body`. + /// + /// A constructor is required to build a `Body` from outside the crate + /// because the `arg_count` and `locals` fields are private. + pub fn new( + blocks: Vec, + locals: LocalDecls, + arg_count: usize, + var_debug_info: Vec, + spread_arg: Option, + span: Span, + ) -> Self { + // If locals doesn't contain enough entries, it can lead to panics in + // `ret_local`, `arg_locals`, and `inner_locals`. + assert!( + locals.len() > arg_count, + "A Body must contain at least a local for the return value and each of the function's arguments" + ); + Self { blocks, locals, arg_count, var_debug_info, spread_arg, span } + } + + /// Return local that holds this function's return value. + pub fn ret_local(&self) -> &LocalDecl { + &self.locals[RETURN_LOCAL] + } + + /// Locals in `self` that correspond to this function's arguments. + pub fn arg_locals(&self) -> &[LocalDecl] { + &self.locals[1..][..self.arg_count] + } + + /// Inner locals for this function. These are the locals that are + /// neither the return local nor the argument locals. + pub fn inner_locals(&self) -> &[LocalDecl] { + &self.locals[self.arg_count + 1..] + } + + /// Returns a mutable reference to the local that holds this function's return value. + pub(crate) fn ret_local_mut(&mut self) -> &mut LocalDecl { + &mut self.locals[RETURN_LOCAL] + } + + /// Returns a mutable slice of locals corresponding to this function's arguments. + pub(crate) fn arg_locals_mut(&mut self) -> &mut [LocalDecl] { + &mut self.locals[1..][..self.arg_count] + } + + /// Returns a mutable slice of inner locals for this function. + /// Inner locals are those that are neither the return local nor the argument locals. + pub(crate) fn inner_locals_mut(&mut self) -> &mut [LocalDecl] { + &mut self.locals[self.arg_count + 1..] + } + + /// Convenience function to get all the locals in this function. + /// + /// Locals are typically accessed via the more specific methods `ret_local`, + /// `arg_locals`, and `inner_locals`. + pub fn locals(&self) -> &[LocalDecl] { + &self.locals + } + + /// Get the local declaration for this local. + pub fn local_decl(&self, local: Local) -> Option<&LocalDecl> { + self.locals.get(local) + } + + /// Get an iterator for all local declarations. + pub fn local_decls(&self) -> impl Iterator { + self.locals.iter().enumerate() + } + + /// Emit the body using the provided name for the signature. + pub fn dump(&self, w: &mut W, fn_name: &str) -> io::Result<()> { + function_body(w, self, fn_name) + } + + pub fn spread_arg(&self) -> Option { + self.spread_arg + } +} + +type LocalDecls = Vec; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct LocalDecl { + pub ty: Ty, + pub span: Span, + pub mutability: Mutability, +} + +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] +pub struct BasicBlock { + pub statements: Vec, + pub terminator: Terminator, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct Terminator { + pub kind: TerminatorKind, + pub span: Span, +} + +impl Terminator { + pub fn successors(&self) -> Successors { + self.kind.successors() + } +} + +pub type Successors = Vec; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum TerminatorKind { + Goto { + target: BasicBlockIdx, + }, + SwitchInt { + discr: Operand, + targets: SwitchTargets, + }, + Resume, + Abort, + Return, + Unreachable, + Drop { + place: Place, + target: BasicBlockIdx, + unwind: UnwindAction, + }, + Call { + func: Operand, + args: Vec, + destination: Place, + target: Option, + unwind: UnwindAction, + }, + Assert { + cond: Operand, + expected: bool, + msg: AssertMessage, + target: BasicBlockIdx, + unwind: UnwindAction, + }, + InlineAsm { + template: String, + operands: Vec, + options: String, + line_spans: String, + destination: Option, + unwind: UnwindAction, + }, +} + +impl TerminatorKind { + pub fn successors(&self) -> Successors { + use self::TerminatorKind::*; + match *self { + Call { target: Some(t), unwind: UnwindAction::Cleanup(u), .. } + | Drop { target: t, unwind: UnwindAction::Cleanup(u), .. } + | Assert { target: t, unwind: UnwindAction::Cleanup(u), .. } + | InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(u), .. } => { + vec![t, u] + } + Goto { target: t } + | Call { target: None, unwind: UnwindAction::Cleanup(t), .. } + | Call { target: Some(t), unwind: _, .. } + | Drop { target: t, unwind: _, .. } + | Assert { target: t, unwind: _, .. } + | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. } + | InlineAsm { destination: Some(t), unwind: _, .. } => { + vec![t] + } + + Return + | Resume + | Abort + | Unreachable + | Call { target: None, unwind: _, .. } + | InlineAsm { destination: None, unwind: _, .. } => { + vec![] + } + SwitchInt { ref targets, .. } => targets.all_targets(), + } + } + + pub fn unwind(&self) -> Option<&UnwindAction> { + match *self { + TerminatorKind::Goto { .. } + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::SwitchInt { .. } => None, + TerminatorKind::Call { ref unwind, .. } + | TerminatorKind::Assert { ref unwind, .. } + | TerminatorKind::Drop { ref unwind, .. } + | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind), + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct InlineAsmOperand { + pub in_value: Option, + pub out_place: Option, + // This field has a raw debug representation of MIR's InlineAsmOperand. + // For now we care about place/operand + the rest in a debug format. + pub raw_rpr: String, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum UnwindAction { + Continue, + Unreachable, + Terminate, + Cleanup(BasicBlockIdx), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AssertMessage { + BoundsCheck { len: Operand, index: Operand }, + Overflow(BinOp, Operand, Operand), + OverflowNeg(Operand), + DivisionByZero(Operand), + RemainderByZero(Operand), + ResumedAfterReturn(CoroutineKind), + ResumedAfterPanic(CoroutineKind), + MisalignedPointerDereference { required: Operand, found: Operand }, + NullPointerDereference, +} + +impl AssertMessage { + pub fn description(&self) -> Result<&'static str, Error> { + match self { + AssertMessage::Overflow(BinOp::Add, _, _) => Ok("attempt to add with overflow"), + AssertMessage::Overflow(BinOp::Sub, _, _) => Ok("attempt to subtract with overflow"), + AssertMessage::Overflow(BinOp::Mul, _, _) => Ok("attempt to multiply with overflow"), + AssertMessage::Overflow(BinOp::Div, _, _) => Ok("attempt to divide with overflow"), + AssertMessage::Overflow(BinOp::Rem, _, _) => { + Ok("attempt to calculate the remainder with overflow") + } + AssertMessage::OverflowNeg(_) => Ok("attempt to negate with overflow"), + AssertMessage::Overflow(BinOp::Shr, _, _) => Ok("attempt to shift right with overflow"), + AssertMessage::Overflow(BinOp::Shl, _, _) => Ok("attempt to shift left with overflow"), + AssertMessage::Overflow(op, _, _) => Err(error!("`{:?}` cannot overflow", op)), + AssertMessage::DivisionByZero(_) => Ok("attempt to divide by zero"), + AssertMessage::RemainderByZero(_) => { + Ok("attempt to calculate the remainder with a divisor of zero") + } + AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { + Ok("coroutine resumed after completion") + } + AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + _, + )) => Ok("`async fn` resumed after completion"), + AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( + CoroutineDesugaring::Gen, + _, + )) => Ok("`async gen fn` resumed after completion"), + AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + _, + )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after completion"), + AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { + Ok("coroutine resumed after panicking") + } + AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + _, + )) => Ok("`async fn` resumed after panicking"), + AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( + CoroutineDesugaring::Gen, + _, + )) => Ok("`async gen fn` resumed after panicking"), + AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + _, + )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"), + + AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"), + AssertMessage::MisalignedPointerDereference { .. } => { + Ok("misaligned pointer dereference") + } + AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"), + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum BinOp { + Add, + AddUnchecked, + Sub, + SubUnchecked, + Mul, + MulUnchecked, + Div, + Rem, + BitXor, + BitAnd, + BitOr, + Shl, + ShlUnchecked, + Shr, + ShrUnchecked, + Eq, + Lt, + Le, + Ne, + Ge, + Gt, + Cmp, + Offset, +} + +impl BinOp { + /// Return the type of this operation for the given input Ty. + /// This function does not perform type checking, and it currently doesn't handle SIMD. + pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty { + with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty)) + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum UnOp { + Not, + Neg, + PtrMetadata, +} + +impl UnOp { + /// Return the type of this operation for the given input Ty. + /// This function does not perform type checking, and it currently doesn't handle SIMD. + pub fn ty(&self, arg_ty: Ty) -> Ty { + with(|ctx| ctx.unop_ty(*self, arg_ty)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum CoroutineKind { + Desugared(CoroutineDesugaring, CoroutineSource), + Coroutine(Movability), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum CoroutineSource { + Block, + Closure, + Fn, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum CoroutineDesugaring { + Async, + + Gen, + + AsyncGen, +} + +pub(crate) type LocalDefId = Opaque; +/// The rustc coverage data structures are heavily tied to internal details of the +/// coverage implementation that are likely to change, and are unlikely to be +/// useful to third-party tools for the foreseeable future. +pub(crate) type Coverage = Opaque; + +/// The FakeReadCause describes the type of pattern why a FakeRead statement exists. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum FakeReadCause { + ForMatchGuard, + ForMatchedPlace(LocalDefId), + ForGuardBinding, + ForLet(LocalDefId), + ForIndex, +} + +/// Describes what kind of retag is to be performed +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] +pub enum RetagKind { + FnEntry, + TwoPhase, + Raw, + Default, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] +pub enum Variance { + Covariant, + Invariant, + Contravariant, + Bivariant, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct CopyNonOverlapping { + pub src: Operand, + pub dst: Operand, + pub count: Operand, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum NonDivergingIntrinsic { + Assume(Operand), + CopyNonOverlapping(CopyNonOverlapping), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct Statement { + pub kind: StatementKind, + pub span: Span, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum StatementKind { + Assign(Place, Rvalue), + FakeRead(FakeReadCause, Place), + SetDiscriminant { place: Place, variant_index: VariantIdx }, + Deinit(Place), + StorageLive(Local), + StorageDead(Local), + Retag(RetagKind, Place), + PlaceMention(Place), + AscribeUserType { place: Place, projections: UserTypeProjection, variance: Variance }, + Coverage(Coverage), + Intrinsic(NonDivergingIntrinsic), + ConstEvalCounter, + Nop, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Rvalue { + /// Creates a pointer with the indicated mutability to the place. + /// + /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like + /// `&raw v` or `addr_of!(v)`. + AddressOf(RawPtrKind, Place), + + /// Creates an aggregate value, like a tuple or struct. + /// + /// This is needed because dataflow analysis needs to distinguish + /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo` + /// has a destructor. + /// + /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After + /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too. + Aggregate(AggregateKind, Vec), + + /// * `Offset` has the same semantics as `<*const T>::offset`, except that the second + /// parameter may be a `usize` as well. + /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats, + /// raw pointers, or function pointers and return a `bool`. The types of the operands must be + /// matching, up to the usual caveat of the lifetimes in function pointers. + /// * Left and right shift operations accept signed or unsigned integers not necessarily of the + /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is + /// truncated as needed. + /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching + /// types and return a value of that type. + /// * The remaining operations accept signed integers, unsigned integers, or floats with + /// matching types and return a value of that type. + BinaryOp(BinOp, Operand, Operand), + + /// Performs essentially all of the casts that can be performed via `as`. + /// + /// This allows for casts from/to a variety of types. + Cast(CastKind, Operand, Ty), + + /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. + /// + /// For addition, subtraction, and multiplication on integers the error condition is set when + /// the infinite precision result would not be equal to the actual result. + CheckedBinaryOp(BinOp, Operand, Operand), + + /// A CopyForDeref is equivalent to a read from a place. + /// When such a read happens, it is guaranteed that the only use of the returned value is a + /// deref operation, immediately followed by one or more projections. + CopyForDeref(Place), + + /// Computes the discriminant of the place, returning it as an integer. + /// Returns zero for types without discriminant. + /// + /// The validity requirements for the underlying value are undecided for this rvalue, see + /// [#91095]. Note too that the value of the discriminant is not the same thing as the + /// variant index; + /// + /// [#91095]: https://github.com/rust-lang/rust/issues/91095 + Discriminant(Place), + + /// Yields the length of the place, as a `usize`. + /// + /// If the type of the place is an array, this is the array length. For slices (`[T]`, not + /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is + /// ill-formed for places of other types. + Len(Place), + + /// Creates a reference to the place. + Ref(Region, BorrowKind, Place), + + /// Creates an array where each element is the value of the operand. + /// + /// This is the cause of a bug in the case where the repetition count is zero because the value + /// is not dropped, see [#74836]. + /// + /// Corresponds to source code like `[x; 32]`. + /// + /// [#74836]: https://github.com/rust-lang/rust/issues/74836 + Repeat(Operand, TyConst), + + /// Transmutes a `*mut u8` into shallow-initialized `Box`. + /// + /// This is different from a normal transmute because dataflow analysis will treat the box as + /// initialized but its content as uninitialized. Like other pointer casts, this in general + /// affects alias analysis. + ShallowInitBox(Operand, Ty), + + /// Creates a pointer/reference to the given thread local. + /// + /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a + /// `*const T`, and if neither of those apply a `&T`. + /// + /// **Note:** This is a runtime operation that actually executes code and is in this sense more + /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to + /// SIGILL for some reason that I (JakobDegen) never got a chance to look into. + /// + /// **Needs clarification**: Are there weird additional semantics here related to the runtime + /// nature of this operation? + ThreadLocalRef(stable_mir::CrateItem), + + /// Computes a value as described by the operation. + NullaryOp(NullOp, Ty), + + /// Exactly like `BinaryOp`, but less operands. + /// + /// Also does two's-complement arithmetic. Negation requires a signed integer or a float; + /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds + /// return a value with the same type as their operand. + UnaryOp(UnOp, Operand), + + /// Yields the operand unchanged + Use(Operand), +} + +impl Rvalue { + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + match self { + Rvalue::Use(operand) => operand.ty(locals), + Rvalue::Repeat(operand, count) => { + Ok(Ty::new_array_with_const_len(operand.ty(locals)?, count.clone())) + } + Rvalue::ThreadLocalRef(did) => Ok(did.ty()), + Rvalue::Ref(reg, bk, place) => { + let place_ty = place.ty(locals)?; + Ok(Ty::new_ref(reg.clone(), place_ty, bk.to_mutable_lossy())) + } + Rvalue::AddressOf(mutability, place) => { + let place_ty = place.ty(locals)?; + Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) + } + Rvalue::Len(..) => Ok(Ty::usize_ty()), + Rvalue::Cast(.., ty) => Ok(*ty), + Rvalue::BinaryOp(op, lhs, rhs) => { + let lhs_ty = lhs.ty(locals)?; + let rhs_ty = rhs.ty(locals)?; + Ok(op.ty(lhs_ty, rhs_ty)) + } + Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + let lhs_ty = lhs.ty(locals)?; + let rhs_ty = rhs.ty(locals)?; + let ty = op.ty(lhs_ty, rhs_ty); + Ok(Ty::new_tuple(&[ty, Ty::bool_ty()])) + } + Rvalue::UnaryOp(op, operand) => { + let arg_ty = operand.ty(locals)?; + Ok(op.ty(arg_ty)) + } + Rvalue::Discriminant(place) => { + let place_ty = place.ty(locals)?; + place_ty + .kind() + .discriminant_ty() + .ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}")) + } + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { + Ok(Ty::usize_ty()) + } + Rvalue::NullaryOp(NullOp::ContractChecks, _) + | Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), + Rvalue::Aggregate(ak, ops) => match *ak { + AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), + AggregateKind::Tuple => Ok(Ty::new_tuple( + &ops.iter().map(|op| op.ty(locals)).collect::, _>>()?, + )), + AggregateKind::Adt(def, _, ref args, _, _) => Ok(def.ty_with_args(args)), + AggregateKind::Closure(def, ref args) => Ok(Ty::new_closure(def, args.clone())), + AggregateKind::Coroutine(def, ref args, mov) => { + Ok(Ty::new_coroutine(def, args.clone(), mov)) + } + AggregateKind::CoroutineClosure(def, ref args) => { + Ok(Ty::new_coroutine_closure(def, args.clone())) + } + AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), + }, + Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), + Rvalue::CopyForDeref(place) => place.ty(locals), + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AggregateKind { + Array(Ty), + Tuple, + Adt(AdtDef, VariantIdx, GenericArgs, Option, Option), + Closure(ClosureDef, GenericArgs), + // FIXME(stable_mir): Movability here is redundant + Coroutine(CoroutineDef, GenericArgs, Movability), + CoroutineClosure(CoroutineClosureDef, GenericArgs), + RawPtr(Ty, Mutability), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Operand { + Copy(Place), + Move(Place), + Constant(ConstOperand), +} + +#[derive(Clone, Eq, PartialEq, Serialize)] +pub struct Place { + pub local: Local, + /// projection out of a place (access a field, deref a pointer, etc) + pub projection: Vec, +} + +impl From for Place { + fn from(local: Local) -> Self { + Place { local, projection: vec![] } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ConstOperand { + pub span: Span, + pub user_ty: Option, + pub const_: MirConst, +} + +/// Debug information pertaining to a user variable. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct VarDebugInfo { + /// The variable name. + pub name: Symbol, + + /// Source info of the user variable, including the scope + /// within which the variable is visible (to debuginfo). + pub source_info: SourceInfo, + + /// The user variable's data is split across several fragments, + /// each described by a `VarDebugInfoFragment`. + pub composite: Option, + + /// Where the data for this user variable is to be found. + pub value: VarDebugInfoContents, + + /// When present, indicates what argument number this variable is in the function that it + /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the + /// argument number in the original function before it was inlined. + pub argument_index: Option, +} + +impl VarDebugInfo { + /// Return a local variable if this info is related to one. + pub fn local(&self) -> Option { + match &self.value { + VarDebugInfoContents::Place(place) if place.projection.is_empty() => Some(place.local), + VarDebugInfoContents::Place(_) | VarDebugInfoContents::Const(_) => None, + } + } + + /// Return a constant if this info is related to one. + pub fn constant(&self) -> Option<&ConstOperand> { + match &self.value { + VarDebugInfoContents::Place(_) => None, + VarDebugInfoContents::Const(const_op) => Some(const_op), + } + } +} + +pub type SourceScope = u32; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct SourceInfo { + pub span: Span, + pub scope: SourceScope, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct VarDebugInfoFragment { + pub ty: Ty, + pub projection: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum VarDebugInfoContents { + Place(Place), + Const(ConstOperand), +} + +// In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This +// is so it can be used for both Places (for which the projection elements are of type +// ProjectionElem) and user-provided type annotations (for which the projection elements +// are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use +// ProjectionElem for Places. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum ProjectionElem { + /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place. + Deref, + + /// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is + /// referenced by source-order index rather than the name of the field. The fields type is also + /// given. + Field(FieldIdx, Ty), + + /// Index into a slice/array. The value of the index is computed at runtime using the `V` + /// argument. + /// + /// Note that this does not also dereference, and so it does not exactly correspond to slice + /// indexing in Rust. In other words, in the below Rust code: + /// + /// ```rust + /// let x = &[1, 2, 3, 4]; + /// let i = 2; + /// x[i]; + /// ``` + /// + /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same + /// thing is true of the `ConstantIndex` and `Subslice` projections below. + Index(Local), + + /// Index into a slice/array given by offsets. + /// + /// These indices are generated by slice patterns. Easiest to explain by example: + /// + /// ```ignore (illustrative) + /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, + /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, + /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, + /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, + /// ``` + ConstantIndex { + /// index or -index (in Python terms), depending on from_end + offset: u64, + /// The thing being indexed must be at least this long -- otherwise, the + /// projection is UB. + /// + /// For arrays this is always the exact length. + min_length: u64, + /// Counting backwards from end? This is always false when indexing an + /// array. + from_end: bool, + }, + + /// Projects a slice from the base place. + /// + /// These indices are generated by slice patterns. If `from_end` is true, this represents + /// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`. + Subslice { + from: u64, + to: u64, + /// Whether `to` counts from the start or end of the array/slice. + from_end: bool, + }, + + /// "Downcast" to a variant of an enum or a coroutine. + Downcast(VariantIdx), + + /// Like an explicit cast from an opaque type to a concrete type, but without + /// requiring an intermediate variable. + OpaqueCast(Ty), + + /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where + /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping + /// explicit during optimizations and codegen. + /// + /// This projection doesn't impact the runtime behavior of the program except for potentially changing + /// some type metadata of the interpreter or codegen backend. + Subtype(Ty), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct UserTypeProjection { + pub base: UserTypeAnnotationIndex, + + pub projection: Opaque, +} + +pub type Local = usize; + +pub const RETURN_LOCAL: Local = 0; + +/// The source-order index of a field in a variant. +/// +/// For example, in the following types, +/// ```ignore(illustrative) +/// enum Demo1 { +/// Variant0 { a: bool, b: i32 }, +/// Variant1 { c: u8, d: u64 }, +/// } +/// struct Demo2 { e: u8, f: u16, g: u8 } +/// ``` +/// `a`'s `FieldIdx` is `0`, +/// `b`'s `FieldIdx` is `1`, +/// `c`'s `FieldIdx` is `0`, and +/// `g`'s `FieldIdx` is `2`. +pub type FieldIdx = usize; + +type UserTypeAnnotationIndex = usize; + +/// The possible branch sites of a [TerminatorKind::SwitchInt]. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct SwitchTargets { + /// The conditional branches where the first element represents the value that guards this + /// branch, and the second element is the branch target. + branches: Vec<(u128, BasicBlockIdx)>, + /// The `otherwise` branch which will be taken in case none of the conditional branches are + /// satisfied. + otherwise: BasicBlockIdx, +} + +impl SwitchTargets { + /// All possible targets including the `otherwise` target. + pub fn all_targets(&self) -> Successors { + self.branches.iter().map(|(_, target)| *target).chain(Some(self.otherwise)).collect() + } + + /// The `otherwise` branch target. + pub fn otherwise(&self) -> BasicBlockIdx { + self.otherwise + } + + /// The conditional targets which are only taken if the pattern matches the given value. + pub fn branches(&self) -> impl Iterator { + self.branches.iter().copied() + } + + /// The number of targets including `otherwise`. + pub fn len(&self) -> usize { + self.branches.len() + 1 + } + + /// Create a new SwitchTargets from the given branches and `otherwise` target. + pub fn new(branches: Vec<(u128, BasicBlockIdx)>, otherwise: BasicBlockIdx) -> SwitchTargets { + SwitchTargets { branches, otherwise } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + Shared, + + /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either + /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]). + Fake(FakeBorrowKind), + + /// Data is mutable and not aliasable. + Mut { + /// `true` if this borrow arose from method-call auto-ref + kind: MutBorrowKind, + }, +} + +impl BorrowKind { + pub fn to_mutable_lossy(self) -> Mutability { + match self { + BorrowKind::Mut { .. } => Mutability::Mut, + BorrowKind::Shared => Mutability::Not, + // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. + BorrowKind::Fake(_) => Mutability::Not, + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum RawPtrKind { + Mut, + Const, + FakeForPtrMetadata, +} + +impl RawPtrKind { + pub fn to_mutable_lossy(self) -> Mutability { + match self { + RawPtrKind::Mut { .. } => Mutability::Mut, + RawPtrKind::Const => Mutability::Not, + // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. + RawPtrKind::FakeForPtrMetadata => Mutability::Not, + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum MutBorrowKind { + Default, + TwoPhaseBorrow, + ClosureCapture, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum FakeBorrowKind { + /// A shared (deep) borrow. Data must be immutable and is aliasable. + Deep, + /// The immediately borrowed place must be immutable, but projections from + /// it don't need to be. This is used to prevent match guards from replacing + /// the scrutinee. For example, a fake borrow of `a.b` doesn't + /// conflict with a mutable borrow of `a.b.c`. + Shallow, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum Mutability { + Not, + Mut, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Safety { + Safe, + Unsafe, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum PointerCoercion { + /// Go from a fn-item type to a fn-pointer type. + ReifyFnPointer, + + /// Go from a safe fn pointer to an unsafe fn pointer. + UnsafeFnPointer, + + /// Go from a non-capturing closure to a fn pointer or an unsafe fn pointer. + /// It cannot convert a closure that requires unsafe. + ClosureFnPointer(Safety), + + /// Go from a mut raw pointer to a const raw pointer. + MutToConstPointer, + + /// Go from `*const [T; N]` to `*const T` + ArrayToPointer, + + /// Unsize a pointer/reference value, e.g., `&[T; n]` to + /// `&[T]`. Note that the source could be a thin or wide pointer. + /// This will do things like convert thin pointers to wide + /// pointers, or convert structs containing thin pointers to + /// structs containing wide pointers, or convert between wide + /// pointers. + Unsize, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum CastKind { + // FIXME(smir-rename): rename this to PointerExposeProvenance + PointerExposeAddress, + PointerWithExposedProvenance, + PointerCoercion(PointerCoercion), + // FIXME(smir-rename): change this to PointerCoercion(DynStar) + DynStar, + IntToInt, + FloatToInt, + FloatToFloat, + IntToFloat, + PtrToPtr, + FnPtrToPtr, + Transmute, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum NullOp { + /// Returns the size of a value of that type. + SizeOf, + /// Returns the minimum alignment of a type. + AlignOf, + /// Returns the offset of a field. + OffsetOf(Vec<(VariantIdx, FieldIdx)>), + /// cfg!(ub_checks), but at codegen time + UbChecks, + /// cfg!(contract_checks), but at codegen time + ContractChecks, +} + +impl Operand { + /// Get the type of an operand relative to the local declaration. + /// + /// In order to retrieve the correct type, the `locals` argument must match the list of all + /// locals from the function body where this operand originates from. + /// + /// Errors indicate a malformed operand or incompatible locals list. + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + match self { + Operand::Copy(place) | Operand::Move(place) => place.ty(locals), + Operand::Constant(c) => Ok(c.ty()), + } + } +} + +impl ConstOperand { + pub fn ty(&self) -> Ty { + self.const_.ty() + } +} + +impl Place { + /// Resolve down the chain of projections to get the type referenced at the end of it. + /// E.g.: + /// Calling `ty()` on `var.field` should return the type of `field`. + /// + /// In order to retrieve the correct type, the `locals` argument must match the list of all + /// locals from the function body where this place originates from. + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty)) + } +} + +impl ProjectionElem { + /// Get the expected type after applying this projection to a given place type. + pub fn ty(&self, place_ty: Ty) -> Result { + let ty = place_ty; + match &self { + ProjectionElem::Deref => Self::deref_ty(ty), + ProjectionElem::Field(_idx, fty) => Ok(*fty), + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => Self::index_ty(ty), + ProjectionElem::Subslice { from, to, from_end } => { + Self::subslice_ty(ty, *from, *to, *from_end) + } + ProjectionElem::Downcast(_) => Ok(ty), + ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), + } + } + + fn index_ty(ty: Ty) -> Result { + ty.kind().builtin_index().ok_or_else(|| error!("Cannot index non-array type: {ty:?}")) + } + + fn subslice_ty(ty: Ty, from: u64, to: u64, from_end: bool) -> Result { + let ty_kind = ty.kind(); + match ty_kind { + TyKind::RigidTy(RigidTy::Slice(..)) => Ok(ty), + TyKind::RigidTy(RigidTy::Array(inner, _)) if !from_end => Ty::try_new_array( + inner, + to.checked_sub(from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?, + ), + TyKind::RigidTy(RigidTy::Array(inner, size)) => { + let size = size.eval_target_usize()?; + let len = size - from - to; + Ty::try_new_array(inner, len) + } + _ => Err(Error(format!("Cannot subslice non-array type: `{ty_kind:?}`"))), + } + } + + fn deref_ty(ty: Ty) -> Result { + let deref_ty = ty + .kind() + .builtin_deref(true) + .ok_or_else(|| error!("Cannot dereference type: {ty:?}"))?; + Ok(deref_ty.ty) + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mir/mono.rs b/compiler/rustc_smir/src/stable_mir/mir/mono.rs new file mode 100644 index 00000000000..0c3e4fb7c90 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir/mono.rs @@ -0,0 +1,308 @@ +use std::fmt::{Debug, Formatter}; +use std::io; + +use serde::Serialize; +use stable_mir::abi::FnAbi; +use stable_mir::crate_def::CrateDef; +use stable_mir::mir::Body; +use stable_mir::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; +use stable_mir::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with}; + +use crate::stable_mir; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum MonoItem { + Fn(Instance), + Static(StaticDef), + GlobalAsm(Opaque), +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize)] +pub struct Instance { + /// The type of instance. + pub kind: InstanceKind, + /// An ID used to get the instance definition from the compiler. + /// Do not use this field directly. + pub def: InstanceDef, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub enum InstanceKind { + /// A user defined item. + Item, + /// A compiler intrinsic function. + Intrinsic, + /// A virtual function definition stored in a VTable. + /// The `idx` field indicates the position in the VTable for this instance. + Virtual { idx: usize }, + /// A compiler generated shim. + Shim, +} + +impl Instance { + /// Get the arguments this instance was instantiated with. + pub fn args(&self) -> GenericArgs { + with(|cx| cx.instance_args(self.def)) + } + + /// Get the body of an Instance. + /// + /// The body will be eagerly monomorphized and all constants will already be evaluated. + /// + /// This method will return the intrinsic fallback body if one was defined. + pub fn body(&self) -> Option { + with(|context| context.instance_body(self.def)) + } + + /// Check whether this instance has a body available. + /// + /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide + /// whether to specialize the intrinsic or to use its fallback body. + /// + /// For more information on fallback body, see . + /// + /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build + /// the StableMIR body. + pub fn has_body(&self) -> bool { + with(|cx| cx.has_body(self.def.def_id())) + } + + pub fn is_foreign_item(&self) -> bool { + with(|cx| cx.is_foreign_item(self.def.def_id())) + } + + /// Get the instance type with generic instantiations applied and lifetimes erased. + pub fn ty(&self) -> Ty { + with(|context| context.instance_ty(self.def)) + } + + /// Retrieve information about this instance binary interface. + pub fn fn_abi(&self) -> Result { + with(|cx| cx.instance_abi(self.def)) + } + + /// Retrieve the instance's mangled name used for calling the given instance. + /// + /// This will also look up the correct name of instances from upstream crates. + pub fn mangled_name(&self) -> Symbol { + with(|context| context.instance_mangled_name(self.def)) + } + + /// Retrieve the instance name for diagnostic messages. + /// + /// This will return the specialized name, e.g., `std::vec::Vec::new`. + pub fn name(&self) -> Symbol { + with(|context| context.instance_name(self.def, false)) + } + + /// Return a trimmed name of the given instance including its args. + /// + /// If a symbol name can only be imported from one place for a type, and as + /// long as it was not glob-imported anywhere in the current crate, we trim its + /// path and print only the name. + pub fn trimmed_name(&self) -> Symbol { + with(|context| context.instance_name(self.def, true)) + } + + /// Retrieve the plain intrinsic name of an instance if it's an intrinsic. + /// + /// The plain name does not include type arguments (as `trimmed_name` does), + /// which is more convenient to match with intrinsic symbols. + pub fn intrinsic_name(&self) -> Option { + match self.kind { + InstanceKind::Intrinsic => { + Some(with(|context| context.intrinsic(self.def.def_id()).unwrap().fn_name())) + } + InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None, + } + } + + /// Resolve an instance starting from a function definition and generic arguments. + pub fn resolve(def: FnDef, args: &GenericArgs) -> Result { + with(|context| { + context + .resolve_instance(def, args) + .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))) + }) + } + + /// Resolve the drop in place for a given type. + pub fn resolve_drop_in_place(ty: Ty) -> Instance { + with(|cx| cx.resolve_drop_in_place(ty)) + } + + /// Resolve an instance for a given function pointer. + pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result { + with(|context| { + context + .resolve_for_fn_ptr(def, args) + .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))) + }) + } + + /// Resolve a closure with the expected kind. + pub fn resolve_closure( + def: ClosureDef, + args: &GenericArgs, + kind: ClosureKind, + ) -> Result { + with(|context| { + context + .resolve_closure(def, args, kind) + .ok_or_else(|| Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`"))) + }) + } + + /// Check whether this instance is an empty shim. + /// + /// Allow users to check if this shim can be ignored when called directly. + /// + /// We have decided not to export different types of Shims to StableMIR users, however, this + /// is a query that can be very helpful for users when processing DropGlue. + /// + /// When generating code for a Drop terminator, users can ignore an empty drop glue. + /// These shims are only needed to generate a valid Drop call done via VTable. + pub fn is_empty_shim(&self) -> bool { + self.kind == InstanceKind::Shim + && with(|cx| { + cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def) + }) + } + + /// Try to constant evaluate the instance into a constant with the given type. + /// + /// This can be used to retrieve a constant that represents an intrinsic return such as + /// `type_id`. + pub fn try_const_eval(&self, const_ty: Ty) -> Result { + with(|cx| cx.eval_instance(self.def, const_ty)) + } + + /// Emit the body of this instance if it has one. + pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { + if let Some(body) = self.body() { body.dump(w, &self.name()) } else { Ok(()) } + } +} + +impl Debug for Instance { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Instance") + .field("kind", &self.kind) + .field("def", &self.mangled_name()) + .field("args", &self.args()) + .finish() + } +} + +/// Try to convert a crate item into an instance. +/// The item cannot be generic in order to be converted into an instance. +impl TryFrom for Instance { + type Error = stable_mir::Error; + + fn try_from(item: CrateItem) -> Result { + with(|context| { + let def_id = item.def_id(); + if !context.requires_monomorphization(def_id) { + Ok(context.mono_instance(def_id)) + } else { + Err(Error::new("Item requires monomorphization".to_string())) + } + }) + } +} + +/// Try to convert an instance into a crate item. +/// Only user defined instances can be converted. +impl TryFrom for CrateItem { + type Error = stable_mir::Error; + + fn try_from(value: Instance) -> Result { + with(|context| { + if value.kind == InstanceKind::Item && context.has_body(value.def.def_id()) { + Ok(CrateItem(context.instance_def_id(value.def))) + } else { + Err(Error::new(format!("Item kind `{:?}` cannot be converted", value.kind))) + } + }) + } +} + +impl From for MonoItem { + fn from(value: Instance) -> Self { + MonoItem::Fn(value) + } +} + +impl From for MonoItem { + fn from(value: StaticDef) -> Self { + MonoItem::Static(value) + } +} + +impl From for CrateItem { + fn from(value: StaticDef) -> Self { + CrateItem(value.0) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct InstanceDef(usize); + +impl CrateDef for InstanceDef { + fn def_id(&self) -> DefId { + with(|context| context.instance_def_id(*self)) + } +} + +crate_def! { + /// Holds information about a static variable definition. + #[derive(Serialize)] + pub StaticDef; +} + +impl TryFrom for StaticDef { + type Error = stable_mir::Error; + + fn try_from(value: CrateItem) -> Result { + if matches!(value.kind(), ItemKind::Static) { + Ok(StaticDef(value.0)) + } else { + Err(Error::new(format!("Expected a static item, but found: {value:?}"))) + } + } +} + +impl TryFrom for StaticDef { + type Error = stable_mir::Error; + + fn try_from(value: Instance) -> Result { + StaticDef::try_from(CrateItem::try_from(value)?) + } +} + +impl From for Instance { + fn from(value: StaticDef) -> Self { + // A static definition should always be convertible to an instance. + with(|cx| cx.mono_instance(value.def_id())) + } +} + +impl StaticDef { + /// Return the type of this static definition. + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } + + /// Evaluate a static's initializer, returning the allocation of the initializer's memory. + pub fn eval_initializer(&self) -> Result { + with(|cx| cx.eval_static_initializer(*self)) + } +} + +impl IndexedVal for InstanceDef { + fn to_val(index: usize) -> Self { + InstanceDef(index) + } + fn to_index(&self) -> usize { + self.0 + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs new file mode 100644 index 00000000000..439ebe978e5 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs @@ -0,0 +1,463 @@ +//! Implement methods to pretty print stable MIR body. +use std::fmt::Debug; +use std::io::Write; +use std::{fmt, io, iter}; + +use fmt::{Display, Formatter}; +use stable_mir::mir::{ + Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents, +}; +use stable_mir::ty::{AdtKind, AssocKind, IndexedVal, MirConst, Ty, TyConst}; +use stable_mir::{Body, CrateDef, Mutability, with}; + +use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind}; +use crate::stable_mir; + +impl Display for Ty { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + with(|ctx| write!(f, "{}", ctx.ty_pretty(*self))) + } +} + +impl Display for AssocKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + AssocKind::Fn => write!(f, "method"), + AssocKind::Const => write!(f, "associated const"), + AssocKind::Type => write!(f, "associated type"), + } + } +} + +impl Debug for Place { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + with(|ctx| write!(f, "{}", ctx.place_pretty(self))) + } +} + +pub(crate) fn function_body(writer: &mut W, body: &Body, name: &str) -> io::Result<()> { + write!(writer, "fn {name}(")?; + let mut sep = ""; + for (index, local) in body.arg_locals().iter().enumerate() { + write!(writer, "{}_{}: {}", sep, index + 1, local.ty)?; + sep = ", "; + } + write!(writer, ")")?; + + let return_local = body.ret_local(); + writeln!(writer, " -> {} {{", return_local.ty)?; + + body.locals().iter().enumerate().try_for_each(|(index, local)| -> io::Result<()> { + if index == 0 || index > body.arg_count { + writeln!(writer, " let {}_{}: {};", pretty_mut(local.mutability), index, local.ty) + } else { + Ok(()) + } + })?; + + body.var_debug_info.iter().try_for_each(|info| { + let content = match &info.value { + VarDebugInfoContents::Place(place) => { + format!("{place:?}") + } + VarDebugInfoContents::Const(constant) => pretty_mir_const(&constant.const_), + }; + writeln!(writer, " debug {} => {};", info.name, content) + })?; + + body.blocks + .iter() + .enumerate() + .map(|(index, block)| -> io::Result<()> { + writeln!(writer, " bb{index}: {{")?; + let _ = block + .statements + .iter() + .map(|statement| -> io::Result<()> { + pretty_statement(writer, &statement.kind)?; + Ok(()) + }) + .collect::>(); + pretty_terminator(writer, &block.terminator.kind)?; + writeln!(writer, " }}").unwrap(); + Ok(()) + }) + .collect::, _>>()?; + writeln!(writer, "}}")?; + Ok(()) +} + +fn pretty_statement(writer: &mut W, statement: &StatementKind) -> io::Result<()> { + const INDENT: &str = " "; + match statement { + StatementKind::Assign(place, rval) => { + write!(writer, "{INDENT}{place:?} = ")?; + pretty_rvalue(writer, rval)?; + writeln!(writer, ";") + } + // FIXME: Add rest of the statements + StatementKind::FakeRead(cause, place) => { + writeln!(writer, "{INDENT}FakeRead({cause:?}, {place:?});") + } + StatementKind::SetDiscriminant { place, variant_index } => { + writeln!(writer, "{INDENT}discriminant({place:?} = {};", variant_index.to_index()) + } + StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"), + StatementKind::StorageLive(local) => { + writeln!(writer, "{INDENT}StorageLive(_{local});") + } + StatementKind::StorageDead(local) => { + writeln!(writer, "{INDENT}StorageDead(_{local});") + } + StatementKind::Retag(kind, place) => writeln!(writer, "Retag({kind:?}, {place:?});"), + StatementKind::PlaceMention(place) => { + writeln!(writer, "{INDENT}PlaceMention({place:?};") + } + StatementKind::ConstEvalCounter => { + writeln!(writer, "{INDENT}ConstEvalCounter;") + } + StatementKind::Nop => writeln!(writer, "{INDENT}nop;"), + StatementKind::AscribeUserType { .. } + | StatementKind::Coverage(_) + | StatementKind::Intrinsic(_) => { + // FIX-ME: Make them pretty. + writeln!(writer, "{INDENT}{statement:?};") + } + } +} + +fn pretty_terminator(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { + pretty_terminator_head(writer, terminator)?; + let successors = terminator.successors(); + let successor_count = successors.len(); + let labels = pretty_successor_labels(terminator); + + let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_))); + let fmt_unwind = |w: &mut W| -> io::Result<()> { + write!(w, "unwind ")?; + match terminator.unwind() { + None | Some(UnwindAction::Cleanup(_)) => unreachable!(), + Some(UnwindAction::Continue) => write!(w, "continue"), + Some(UnwindAction::Unreachable) => write!(w, "unreachable"), + Some(UnwindAction::Terminate) => write!(w, "terminate"), + } + }; + + match (successor_count, show_unwind) { + (0, false) => {} + (0, true) => { + write!(writer, " -> ")?; + fmt_unwind(writer)?; + } + (1, false) => write!(writer, " -> bb{:?}", successors[0])?, + _ => { + write!(writer, " -> [")?; + for (i, target) in successors.iter().enumerate() { + if i > 0 { + write!(writer, ", ")?; + } + write!(writer, "{}: bb{:?}", labels[i], target)?; + } + if show_unwind { + write!(writer, ", ")?; + fmt_unwind(writer)?; + } + write!(writer, "]")?; + } + }; + + writeln!(writer, ";") +} + +fn pretty_terminator_head(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { + use self::TerminatorKind::*; + const INDENT: &str = " "; + match terminator { + Goto { .. } => write!(writer, "{INDENT}goto"), + SwitchInt { discr, .. } => { + write!(writer, "{INDENT}switchInt({})", pretty_operand(discr)) + } + Resume => write!(writer, "{INDENT}resume"), + Abort => write!(writer, "{INDENT}abort"), + Return => write!(writer, "{INDENT}return"), + Unreachable => write!(writer, "{INDENT}unreachable"), + Drop { place, .. } => write!(writer, "{INDENT}drop({place:?})"), + Call { func, args, destination, .. } => { + write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?; + let mut args_iter = args.iter(); + args_iter.next().map_or(Ok(()), |arg| write!(writer, "{}", pretty_operand(arg)))?; + args_iter.try_for_each(|arg| write!(writer, ", {}", pretty_operand(arg)))?; + write!(writer, ")") + } + Assert { cond, expected, msg, target: _, unwind: _ } => { + write!(writer, "{INDENT}assert(")?; + if !expected { + write!(writer, "!")?; + } + write!(writer, "{}, ", pretty_operand(cond))?; + pretty_assert_message(writer, msg)?; + write!(writer, ")") + } + InlineAsm { .. } => write!(writer, "{INDENT}InlineAsm"), + } +} + +fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec { + use self::TerminatorKind::*; + match terminator { + Call { target: None, unwind: UnwindAction::Cleanup(_), .. } + | InlineAsm { destination: None, .. } => vec!["unwind".into()], + Resume | Abort | Return | Unreachable | Call { target: None, unwind: _, .. } => vec![], + Goto { .. } => vec!["".to_string()], + SwitchInt { targets, .. } => targets + .branches() + .map(|(val, _target)| format!("{val}")) + .chain(iter::once("otherwise".into())) + .collect(), + Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()], + Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => { + vec!["return".into(), "unwind".into()] + } + Drop { unwind: _, .. } | Call { target: Some(_), unwind: _, .. } => vec!["return".into()], + Assert { unwind: UnwindAction::Cleanup(_), .. } => { + vec!["success".into(), "unwind".into()] + } + Assert { unwind: _, .. } => vec!["success".into()], + InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()], + } +} + +fn pretty_assert_message(writer: &mut W, msg: &AssertMessage) -> io::Result<()> { + match msg { + AssertMessage::BoundsCheck { len, index } => { + let pretty_len = pretty_operand(len); + let pretty_index = pretty_operand(index); + write!( + writer, + "\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}" + ) + } + AssertMessage::Overflow(BinOp::Add, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Sub, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Mul, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Div, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Rem, l, r) => { + let pretty_l = pretty_operand(l); + let pretty_r = pretty_operand(r); + write!( + writer, + "\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) + } + AssertMessage::Overflow(BinOp::Shr, _, r) => { + let pretty_r = pretty_operand(r); + write!(writer, "\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}") + } + AssertMessage::Overflow(BinOp::Shl, _, r) => { + let pretty_r = pretty_operand(r); + write!(writer, "\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}") + } + AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op), + AssertMessage::OverflowNeg(op) => { + let pretty_op = pretty_operand(op); + write!(writer, "\"attempt to negate `{{}}`, which would overflow\", {pretty_op}") + } + AssertMessage::DivisionByZero(op) => { + let pretty_op = pretty_operand(op); + write!(writer, "\"attempt to divide `{{}}` by zero\", {pretty_op}") + } + AssertMessage::RemainderByZero(op) => { + let pretty_op = pretty_operand(op); + write!( + writer, + "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}" + ) + } + AssertMessage::MisalignedPointerDereference { required, found } => { + let pretty_required = pretty_operand(required); + let pretty_found = pretty_operand(found); + write!( + writer, + "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}" + ) + } + AssertMessage::NullPointerDereference => { + write!(writer, "\"null pointer dereference occurred\"") + } + AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { + write!(writer, "{}", msg.description().unwrap()) + } + } +} + +fn pretty_operand(operand: &Operand) -> String { + match operand { + Operand::Copy(copy) => { + format!("{copy:?}") + } + Operand::Move(mv) => { + format!("move {mv:?}") + } + Operand::Constant(cnst) => pretty_mir_const(&cnst.const_), + } +} + +fn pretty_mir_const(literal: &MirConst) -> String { + with(|cx| cx.mir_const_pretty(literal)) +} + +fn pretty_ty_const(ct: &TyConst) -> String { + with(|cx| cx.ty_const_pretty(ct.id)) +} + +fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { + match rval { + Rvalue::AddressOf(mutability, place) => { + write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place) + } + Rvalue::Aggregate(aggregate_kind, operands) => { + // FIXME: Add pretty_aggregate function that returns a pretty string + pretty_aggregate(writer, aggregate_kind, operands) + } + Rvalue::BinaryOp(bin, op1, op2) => { + write!(writer, "{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) + } + Rvalue::Cast(_, op, ty) => { + write!(writer, "{} as {}", pretty_operand(op), ty) + } + Rvalue::CheckedBinaryOp(bin, op1, op2) => { + write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) + } + Rvalue::CopyForDeref(deref) => { + write!(writer, "CopyForDeref({deref:?})") + } + Rvalue::Discriminant(place) => { + write!(writer, "discriminant({place:?})") + } + Rvalue::Len(len) => { + write!(writer, "len({len:?})") + } + Rvalue::Ref(_, borrowkind, place) => { + let kind = match borrowkind { + BorrowKind::Shared => "&", + BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ", + BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ", + BorrowKind::Mut { .. } => "&mut ", + }; + write!(writer, "{kind}{place:?}") + } + Rvalue::Repeat(op, cnst) => { + write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst)) + } + Rvalue::ShallowInitBox(_, _) => Ok(()), + Rvalue::ThreadLocalRef(item) => { + write!(writer, "thread_local_ref{item:?}") + } + Rvalue::NullaryOp(nul, ty) => { + write!(writer, "{nul:?}::<{ty}>() \" \"") + } + Rvalue::UnaryOp(un, op) => { + write!(writer, "{:?}({})", un, pretty_operand(op)) + } + Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)), + } +} + +fn pretty_aggregate( + writer: &mut W, + aggregate_kind: &AggregateKind, + operands: &Vec, +) -> io::Result<()> { + let suffix = match aggregate_kind { + AggregateKind::Array(_) => { + write!(writer, "[")?; + "]" + } + AggregateKind::Tuple => { + write!(writer, "(")?; + ")" + } + AggregateKind::Adt(def, var, _, _, _) => { + if def.kind() == AdtKind::Enum { + write!(writer, "{}::{}", def.name(), def.variant(*var).unwrap().name())?; + } else { + write!(writer, "{}", def.variant(*var).unwrap().name())?; + } + if operands.is_empty() { + return Ok(()); + } + // FIXME: Change this once we have CtorKind in StableMIR. + write!(writer, "(")?; + ")" + } + AggregateKind::Closure(def, _) => { + write!(writer, "{{closure@{}}}(", def.span().diagnostic())?; + ")" + } + AggregateKind::Coroutine(def, _, _) => { + write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?; + ")" + } + AggregateKind::CoroutineClosure(def, _) => { + write!(writer, "{{coroutine-closure@{}}}(", def.span().diagnostic())?; + ")" + } + AggregateKind::RawPtr(ty, mutability) => { + write!( + writer, + "*{} {ty} from (", + if *mutability == Mutability::Mut { "mut" } else { "const" } + )?; + ")" + } + }; + let mut separator = ""; + for op in operands { + write!(writer, "{}{}", separator, pretty_operand(op))?; + separator = ", "; + } + write!(writer, "{suffix}") +} + +fn pretty_mut(mutability: Mutability) -> &'static str { + match mutability { + Mutability::Not => " ", + Mutability::Mut => "mut ", + } +} + +fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str { + match kind { + RawPtrKind::Const => "const", + RawPtrKind::Mut => "mut", + RawPtrKind::FakeForPtrMetadata => "const (fake)", + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mir/visit.rs b/compiler/rustc_smir/src/stable_mir/mir/visit.rs new file mode 100644 index 00000000000..786693ea98d --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mir/visit.rs @@ -0,0 +1,588 @@ +//! # The Stable MIR Visitor +//! +//! ## Overview +//! +//! We currently only support an immutable visitor. +//! The structure of this visitor is similar to the ones internal to `rustc`, +//! and it follows the following conventions: +//! +//! For every mir item, the trait has a `visit_` and a `super_` method. +//! - `visit_`, by default, calls `super_` +//! - `super_`, by default, destructures the `` and calls `visit_` for +//! all sub-items that compose the original item. +//! +//! In order to implement a visitor, override the `visit_*` methods for the types you are +//! interested in analyzing, and invoke (within that method call) +//! `self.super_*` to continue to the traverse. +//! Avoid calling `super` methods in other circumstances. +//! +//! For the most part, we do not destructure things external to the +//! MIR, e.g., types, spans, etc, but simply visit them and stop. +//! This avoids duplication with other visitors like `TypeFoldable`. +//! +//! ## Updating +//! +//! The code is written in a very deliberate style intended to minimize +//! the chance of things being overlooked. +//! +//! Use pattern matching to reference fields and ensure that all +//! matches are exhaustive. +//! +//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. +//! That means you never write `..` to skip over fields, nor do you write `_` +//! to skip over variants in a `match`. +//! +//! The only place that `_` is acceptable is to match a field (or +//! variant argument) that does not require visiting. + +use stable_mir::mir::*; +use stable_mir::ty::{GenericArgs, MirConst, Region, Ty, TyConst}; +use stable_mir::{Error, Opaque, Span}; + +use crate::stable_mir; + +macro_rules! make_mir_visitor { + ($visitor_trait_name:ident, $($mutability:ident)?) => { + pub trait $visitor_trait_name { + fn visit_body(&mut self, body: &$($mutability)? Body) { + self.super_body(body) + } + + fn visit_basic_block(&mut self, bb: &$($mutability)? BasicBlock) { + self.super_basic_block(bb) + } + + fn visit_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_ret_decl(local, decl) + } + + fn visit_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_arg_decl(local, decl) + } + + fn visit_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_local_decl(local, decl) + } + + fn visit_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) { + self.super_statement(stmt, location) + } + + fn visit_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) { + self.super_terminator(term, location) + } + + fn visit_span(&mut self, span: &$($mutability)? Span) { + self.super_span(span) + } + + fn visit_place(&mut self, place: &$($mutability)? Place, ptx: PlaceContext, location: Location) { + self.super_place(place, ptx, location) + } + + visit_place_fns!($($mutability)?); + + fn visit_local(&mut self, local: &$($mutability)? Local, ptx: PlaceContext, location: Location) { + let _ = (local, ptx, location); + } + + fn visit_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) { + self.super_rvalue(rvalue, location) + } + + fn visit_operand(&mut self, operand: &$($mutability)? Operand, location: Location) { + self.super_operand(operand, location) + } + + fn visit_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) { + self.super_user_type_projection(projection) + } + + fn visit_ty(&mut self, ty: &$($mutability)? Ty, location: Location) { + let _ = location; + self.super_ty(ty) + } + + fn visit_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) { + self.super_const_operand(constant, location) + } + + fn visit_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) { + self.super_mir_const(constant, location) + } + + fn visit_ty_const(&mut self, constant: &$($mutability)? TyConst, location: Location) { + let _ = location; + self.super_ty_const(constant) + } + + fn visit_region(&mut self, region: &$($mutability)? Region, location: Location) { + let _ = location; + self.super_region(region) + } + + fn visit_args(&mut self, args: &$($mutability)? GenericArgs, location: Location) { + let _ = location; + self.super_args(args) + } + + fn visit_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) { + self.super_assert_msg(msg, location) + } + + fn visit_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) { + self.super_var_debug_info(var_debug_info); + } + + fn super_body(&mut self, body: &$($mutability)? Body) { + super_body!(self, body, $($mutability)?); + } + + fn super_basic_block(&mut self, bb: &$($mutability)? BasicBlock) { + let BasicBlock { statements, terminator } = bb; + for stmt in statements { + self.visit_statement(stmt, Location(stmt.span)); + } + self.visit_terminator(terminator, Location(terminator.span)); + } + + fn super_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + let _ = local; + let LocalDecl { ty, span, .. } = decl; + self.visit_ty(ty, Location(*span)); + } + + fn super_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_local_decl(local, decl) + } + + fn super_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { + self.super_local_decl(local, decl) + } + + fn super_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) { + let Statement { kind, span } = stmt; + self.visit_span(span); + match kind { + StatementKind::Assign(place, rvalue) => { + self.visit_place(place, PlaceContext::MUTATING, location); + self.visit_rvalue(rvalue, location); + } + StatementKind::FakeRead(_, place) | StatementKind::PlaceMention(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location); + } + StatementKind::SetDiscriminant { place, .. } + | StatementKind::Deinit(place) + | StatementKind::Retag(_, place) => { + self.visit_place(place, PlaceContext::MUTATING, location); + } + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + self.visit_local(local, PlaceContext::NON_USE, location); + } + StatementKind::AscribeUserType { place, projections, variance: _ } => { + self.visit_place(place, PlaceContext::NON_USE, location); + self.visit_user_type_projection(projections); + } + StatementKind::Coverage(coverage) => visit_opaque(coverage), + StatementKind::Intrinsic(intrisic) => match intrisic { + NonDivergingIntrinsic::Assume(operand) => { + self.visit_operand(operand, location); + } + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src, + dst, + count, + }) => { + self.visit_operand(src, location); + self.visit_operand(dst, location); + self.visit_operand(count, location); + } + }, + StatementKind::ConstEvalCounter | StatementKind::Nop => {} + } + } + + fn super_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) { + let Terminator { kind, span } = term; + self.visit_span(span); + match kind { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Unreachable => {} + TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { + self.visit_operand(cond, location); + self.visit_assert_msg(msg, location); + } + TerminatorKind::Drop { place, target: _, unwind: _ } => { + self.visit_place(place, PlaceContext::MUTATING, location); + } + TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => { + self.visit_operand(func, location); + for arg in args { + self.visit_operand(arg, location); + } + self.visit_place(destination, PlaceContext::MUTATING, location); + } + TerminatorKind::InlineAsm { operands, .. } => { + for op in operands { + let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op; + if let Some(input) = in_value { + self.visit_operand(input, location); + } + if let Some(output) = out_place { + self.visit_place(output, PlaceContext::MUTATING, location); + } + } + } + TerminatorKind::Return => { + let $($mutability)? local = RETURN_LOCAL; + self.visit_local(&$($mutability)? local, PlaceContext::NON_MUTATING, location); + } + TerminatorKind::SwitchInt { discr, targets: _ } => { + self.visit_operand(discr, location); + } + } + } + + fn super_span(&mut self, span: &$($mutability)? Span) { + let _ = span; + } + + fn super_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) { + match rvalue { + Rvalue::AddressOf(mutability, place) => { + let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut }; + self.visit_place(place, pcx, location); + } + Rvalue::Aggregate(_, operands) => { + for op in operands { + self.visit_operand(op, location); + } + } + Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { + self.visit_operand(lhs, location); + self.visit_operand(rhs, location); + } + Rvalue::Cast(_, op, ty) => { + self.visit_operand(op, location); + self.visit_ty(ty, location); + } + Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location); + } + Rvalue::Ref(region, kind, place) => { + self.visit_region(region, location); + let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) }; + self.visit_place(place, pcx, location); + } + Rvalue::Repeat(op, constant) => { + self.visit_operand(op, location); + self.visit_ty_const(constant, location); + } + Rvalue::ShallowInitBox(op, ty) => { + self.visit_ty(ty, location); + self.visit_operand(op, location) + } + Rvalue::ThreadLocalRef(_) => {} + Rvalue::NullaryOp(_, ty) => { + self.visit_ty(ty, location); + } + Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => { + self.visit_operand(op, location); + } + } + } + + fn super_operand(&mut self, operand: &$($mutability)? Operand, location: Location) { + match operand { + Operand::Copy(place) | Operand::Move(place) => { + self.visit_place(place, PlaceContext::NON_MUTATING, location) + } + Operand::Constant(constant) => { + self.visit_const_operand(constant, location); + } + } + } + + fn super_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) { + // This is a no-op on mir::Visitor. + let _ = projection; + } + + fn super_ty(&mut self, ty: &$($mutability)? Ty) { + let _ = ty; + } + + fn super_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) { + let ConstOperand { span, user_ty: _, const_ } = constant; + self.visit_span(span); + self.visit_mir_const(const_, location); + } + + fn super_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) { + let MirConst { kind: _, ty, id: _ } = constant; + self.visit_ty(ty, location); + } + + fn super_ty_const(&mut self, constant: &$($mutability)? TyConst) { + let _ = constant; + } + + fn super_region(&mut self, region: &$($mutability)? Region) { + let _ = region; + } + + fn super_args(&mut self, args: &$($mutability)? GenericArgs) { + let _ = args; + } + + fn super_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) { + let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } = + var_debug_info; + self.visit_span(&$($mutability)? source_info.span); + let location = Location(source_info.span); + if let Some(composite) = composite { + self.visit_ty(&$($mutability)? composite.ty, location); + } + match value { + VarDebugInfoContents::Place(place) => { + self.visit_place(place, PlaceContext::NON_USE, location); + } + VarDebugInfoContents::Const(constant) => { + self.visit_mir_const(&$($mutability)? constant.const_, location); + } + } + } + + fn super_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) { + match msg { + AssertMessage::BoundsCheck { len, index } => { + self.visit_operand(len, location); + self.visit_operand(index, location); + } + AssertMessage::Overflow(_, left, right) => { + self.visit_operand(left, location); + self.visit_operand(right, location); + } + AssertMessage::OverflowNeg(op) + | AssertMessage::DivisionByZero(op) + | AssertMessage::RemainderByZero(op) => { + self.visit_operand(op, location); + } + AssertMessage::ResumedAfterReturn(_) + | AssertMessage::ResumedAfterPanic(_) + | AssertMessage::NullPointerDereference => { + //nothing to visit + } + AssertMessage::MisalignedPointerDereference { required, found } => { + self.visit_operand(required, location); + self.visit_operand(found, location); + } + } + } + } + }; +} + +macro_rules! super_body { + ($self:ident, $body:ident, mut) => { + for bb in $body.blocks.iter_mut() { + $self.visit_basic_block(bb); + } + + $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local_mut()); + + for (idx, arg) in $body.arg_locals_mut().iter_mut().enumerate() { + $self.visit_arg_decl(idx + 1, arg) + } + + let local_start = $body.arg_count + 1; + for (idx, arg) in $body.inner_locals_mut().iter_mut().enumerate() { + $self.visit_local_decl(idx + local_start, arg) + } + + for info in $body.var_debug_info.iter_mut() { + $self.visit_var_debug_info(info); + } + + $self.visit_span(&mut $body.span) + }; + + ($self:ident, $body:ident, ) => { + let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = $body; + + for bb in blocks { + $self.visit_basic_block(bb); + } + + $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local()); + + for (idx, arg) in $body.arg_locals().iter().enumerate() { + $self.visit_arg_decl(idx + 1, arg) + } + + let local_start = arg_count + 1; + for (idx, arg) in $body.inner_locals().iter().enumerate() { + $self.visit_local_decl(idx + local_start, arg) + } + + for info in var_debug_info.iter() { + $self.visit_var_debug_info(info); + } + + $self.visit_span(span) + }; +} + +macro_rules! visit_place_fns { + (mut) => { + fn super_place(&mut self, place: &mut Place, ptx: PlaceContext, location: Location) { + self.visit_local(&mut place.local, ptx, location); + + for elem in place.projection.iter_mut() { + self.visit_projection_elem(elem, ptx, location); + } + } + + // We don't have to replicate the `process_projection()` like we did in + // `rustc_middle::mir::visit.rs` here because the `projection` field in `Place` + // of Stable-MIR is not an immutable borrow, unlike in `Place` of MIR. + fn visit_projection_elem( + &mut self, + elem: &mut ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + self.super_projection_elem(elem, ptx, location) + } + + fn super_projection_elem( + &mut self, + elem: &mut ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Deref => {} + ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location), + ProjectionElem::Index(local) => self.visit_local(local, ptx, location), + ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {} + ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} + ProjectionElem::Downcast(_idx) => {} + ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), + ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), + } + } + }; + + () => { + fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { + self.visit_local(&place.local, ptx, location); + + for (idx, elem) in place.projection.iter().enumerate() { + let place_ref = + PlaceRef { local: place.local, projection: &place.projection[..idx] }; + self.visit_projection_elem(place_ref, elem, ptx, location); + } + } + + fn visit_projection_elem<'a>( + &mut self, + place_ref: PlaceRef<'a>, + elem: &ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + let _ = place_ref; + self.super_projection_elem(elem, ptx, location); + } + + fn super_projection_elem( + &mut self, + elem: &ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Deref => {} + ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location), + ProjectionElem::Index(local) => self.visit_local(local, ptx, location), + ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {} + ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} + ProjectionElem::Downcast(_idx) => {} + ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), + ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), + } + } + }; +} + +make_mir_visitor!(MirVisitor,); +make_mir_visitor!(MutMirVisitor, mut); + +/// This function is a no-op that gets used to ensure this visitor is kept up-to-date. +/// +/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail +/// when trying to invoke `visit_opaque`. +/// +/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()` +/// by a `visit_` for your construct. +fn visit_opaque(_: &Opaque) {} + +/// The location of a statement / terminator in the code and the CFG. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct Location(Span); + +impl Location { + pub fn span(&self) -> Span { + self.0 + } +} + +/// Location of the statement at the given index for a given basic block. Assumes that `stmt_idx` +/// and `bb_idx` are valid for a given body. +pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location { + let bb = &body.blocks[*bb_idx]; + let stmt = &bb.statements[stmt_idx]; + Location(stmt.span) +} + +/// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given +/// body. +pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location { + let bb = &body.blocks[*bb_idx]; + let terminator = &bb.terminator; + Location(terminator.span) +} + +/// Reference to a place used to represent a partial projection. +pub struct PlaceRef<'a> { + pub local: Local, + pub projection: &'a [ProjectionElem], +} + +impl PlaceRef<'_> { + /// Get the type of this place. + pub fn ty(&self, locals: &[LocalDecl]) -> Result { + self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty)) + } +} + +/// Information about a place's usage. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct PlaceContext { + /// Whether the access is mutable or not. Keep this private so we can increment the type in a + /// backward compatible manner. + is_mut: bool, +} + +impl PlaceContext { + const MUTATING: Self = PlaceContext { is_mut: true }; + const NON_MUTATING: Self = PlaceContext { is_mut: false }; + const NON_USE: Self = PlaceContext { is_mut: false }; + + pub fn is_mutating(&self) -> bool { + self.is_mut + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs new file mode 100644 index 00000000000..c59758d4ad3 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -0,0 +1,239 @@ +//! Module that is temporarily parasitic on the `rustc_smir` crate, +//! +//! This module is designed to resolve circular dependency that would happen +//! if we gradually invert the dependency order between `rustc_smir` and `stable_mir`. +//! +//! Once refactoring is complete, we will migrate it back to the `stable_mir` crate. + +//! The WIP stable interface to rustc internals. +//! +//! For more information see +//! +//! # Note +//! +//! This API is still completely unstable and subject to change. + +// #![doc( +// html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", +// test(attr(allow(unused_variables), deny(warnings))) +// )] +//! +//! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to +//! interact with the compiler. +//! +//! The goal is to eventually be published on +//! [crates.io](https://crates.io). + +use std::fmt::Debug; +use std::{fmt, io}; + +use serde::Serialize; +use stable_mir::compiler_interface::with; +pub use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId}; +pub use stable_mir::error::*; +use stable_mir::mir::mono::StaticDef; +use stable_mir::mir::{Body, Mutability}; +use stable_mir::ty::{AssocItem, FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty}; + +use crate::stable_mir; + +pub mod abi; +#[macro_use] +pub mod crate_def; +pub mod compiler_interface; +#[macro_use] +pub mod error; +pub mod mir; +pub mod target; +pub mod ty; +pub mod visitor; + +/// Use String for now but we should replace it. +pub type Symbol = String; + +/// The number that identifies a crate. +pub type CrateNum = usize; + +impl Debug for DefId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DefId").field("id", &self.0).field("name", &self.name()).finish() + } +} + +impl IndexedVal for DefId { + fn to_val(index: usize) -> Self { + DefId(index) + } + + fn to_index(&self) -> usize { + self.0 + } +} + +/// A list of crate items. +pub type CrateItems = Vec; + +/// A list of trait decls. +pub type TraitDecls = Vec; + +/// A list of impl trait decls. +pub type ImplTraitDecls = Vec; + +/// A list of associated items. +pub type AssocItems = Vec; + +/// Holds information about a crate. +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] +pub struct Crate { + pub id: CrateNum, + pub name: Symbol, + pub is_local: bool, +} + +impl Crate { + /// The list of foreign modules in this crate. + pub fn foreign_modules(&self) -> Vec { + with(|cx| cx.foreign_modules(self.id)) + } + + /// The list of traits declared in this crate. + pub fn trait_decls(&self) -> TraitDecls { + with(|cx| cx.trait_decls(self.id)) + } + + /// The list of trait implementations in this crate. + pub fn trait_impls(&self) -> ImplTraitDecls { + with(|cx| cx.trait_impls(self.id)) + } + + /// Return a list of function definitions from this crate independent on their visibility. + pub fn fn_defs(&self) -> Vec { + with(|cx| cx.crate_functions(self.id)) + } + + /// Return a list of static items defined in this crate independent on their visibility. + pub fn statics(&self) -> Vec { + with(|cx| cx.crate_statics(self.id)) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] +pub enum ItemKind { + Fn, + Static, + Const, + Ctor(CtorKind), +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] +pub enum CtorKind { + Const, + Fn, +} + +pub type Filename = String; + +crate_def_with_ty! { + /// Holds information about an item in a crate. + #[derive(Serialize)] + pub CrateItem; +} + +impl CrateItem { + /// This will return the body of an item or panic if it's not available. + pub fn expect_body(&self) -> mir::Body { + with(|cx| cx.mir_body(self.0)) + } + + /// Return the body of an item if available. + pub fn body(&self) -> Option { + with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0))) + } + + /// Check if a body is available for this item. + pub fn has_body(&self) -> bool { + with(|cx| cx.has_body(self.0)) + } + + pub fn span(&self) -> Span { + with(|cx| cx.span_of_an_item(self.0)) + } + + pub fn kind(&self) -> ItemKind { + with(|cx| cx.item_kind(*self)) + } + + pub fn requires_monomorphization(&self) -> bool { + with(|cx| cx.requires_monomorphization(self.0)) + } + + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } + + pub fn is_foreign_item(&self) -> bool { + with(|cx| cx.is_foreign_item(self.0)) + } + + /// Emit MIR for this item body. + pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { + self.body() + .ok_or_else(|| io::Error::other(format!("No body found for `{}`", self.name())))? + .dump(w, &self.name()) + } +} + +/// Return the function where execution starts if the current +/// crate defines that. This is usually `main`, but could be +/// `start` if the crate is a no-std crate. +pub fn entry_fn() -> Option { + with(|cx| cx.entry_fn()) +} + +/// Access to the local crate. +pub fn local_crate() -> Crate { + with(|cx| cx.local_crate()) +} + +/// Try to find a crate or crates if multiple crates exist from given name. +pub fn find_crates(name: &str) -> Vec { + with(|cx| cx.find_crates(name)) +} + +/// Try to find a crate with the given name. +pub fn external_crates() -> Vec { + with(|cx| cx.external_crates()) +} + +/// Retrieve all items in the local crate that have a MIR associated with them. +pub fn all_local_items() -> CrateItems { + with(|cx| cx.all_local_items()) +} + +pub fn all_trait_decls() -> TraitDecls { + with(|cx| cx.all_trait_decls()) +} + +pub fn all_trait_impls() -> ImplTraitDecls { + with(|cx| cx.all_trait_impls()) +} + +/// A type that provides internal information but that can still be used for debug purpose. +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub struct Opaque(String); + +impl std::fmt::Display for Opaque { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::Debug for Opaque { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +pub fn opaque(value: &T) -> Opaque { + Opaque(format!("{value:?}")) +} diff --git a/compiler/rustc_smir/src/stable_mir/target.rs b/compiler/rustc_smir/src/stable_mir/target.rs new file mode 100644 index 00000000000..6cf1e9feb01 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/target.rs @@ -0,0 +1,61 @@ +//! Provide information about the machine that this is being compiled into. + +use serde::Serialize; +use stable_mir::compiler_interface::with; + +use crate::stable_mir; + +/// The properties of the target machine being compiled into. +#[derive(Clone, PartialEq, Eq, Serialize)] +pub struct MachineInfo { + pub endian: Endian, + pub pointer_width: MachineSize, +} + +impl MachineInfo { + pub fn target() -> MachineInfo { + with(|cx| cx.target_info()) + } + + pub fn target_endianness() -> Endian { + with(|cx| cx.target_info().endian) + } + + pub fn target_pointer_width() -> MachineSize { + with(|cx| cx.target_info().pointer_width) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Serialize)] +pub enum Endian { + Little, + Big, +} + +/// Represent the size of a component. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] +pub struct MachineSize { + num_bits: usize, +} + +impl MachineSize { + #[inline(always)] + pub fn bytes(self) -> usize { + self.num_bits / 8 + } + + #[inline(always)] + pub fn bits(self) -> usize { + self.num_bits + } + + #[inline(always)] + pub fn from_bits(num_bits: usize) -> MachineSize { + MachineSize { num_bits } + } + + #[inline] + pub fn unsigned_int_max(self) -> Option { + (self.num_bits <= 128).then(|| u128::MAX >> (128 - self.bits())) + } +} diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs new file mode 100644 index 00000000000..1efa2fe13c5 --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -0,0 +1,1623 @@ +use std::fmt::{self, Debug, Display, Formatter}; +use std::ops::Range; + +use serde::Serialize; +use stable_mir::abi::{FnAbi, Layout}; +use stable_mir::crate_def::{CrateDef, CrateDefItems, CrateDefType}; +use stable_mir::mir::alloc::{AllocId, read_target_int, read_target_uint}; +use stable_mir::mir::mono::StaticDef; +use stable_mir::target::MachineInfo; +use stable_mir::{Filename, Opaque}; + +use super::mir::{Body, Mutability, Safety}; +use super::{DefId, Error, Symbol, with}; +use crate::stable_mir; + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] +pub struct Ty(usize); + +impl Debug for Ty { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Ty").field("id", &self.0).field("kind", &self.kind()).finish() + } +} + +/// Constructors for `Ty`. +impl Ty { + /// Create a new type from a given kind. + pub fn from_rigid_kind(kind: RigidTy) -> Ty { + with(|cx| cx.new_rigid_ty(kind)) + } + + /// Create a new array type. + pub fn try_new_array(elem_ty: Ty, size: u64) -> Result { + Ok(Ty::from_rigid_kind(RigidTy::Array(elem_ty, TyConst::try_from_target_usize(size)?))) + } + + /// Create a new array type from Const length. + pub fn new_array_with_const_len(elem_ty: Ty, len: TyConst) -> Ty { + Ty::from_rigid_kind(RigidTy::Array(elem_ty, len)) + } + + /// Create a new pointer type. + pub fn new_ptr(pointee_ty: Ty, mutability: Mutability) -> Ty { + Ty::from_rigid_kind(RigidTy::RawPtr(pointee_ty, mutability)) + } + + /// Create a new reference type. + pub fn new_ref(reg: Region, pointee_ty: Ty, mutability: Mutability) -> Ty { + Ty::from_rigid_kind(RigidTy::Ref(reg, pointee_ty, mutability)) + } + + /// Create a new pointer type. + pub fn new_tuple(tys: &[Ty]) -> Ty { + Ty::from_rigid_kind(RigidTy::Tuple(Vec::from(tys))) + } + + /// Create a new closure type. + pub fn new_closure(def: ClosureDef, args: GenericArgs) -> Ty { + Ty::from_rigid_kind(RigidTy::Closure(def, args)) + } + + /// Create a new coroutine type. + pub fn new_coroutine(def: CoroutineDef, args: GenericArgs, mov: Movability) -> Ty { + Ty::from_rigid_kind(RigidTy::Coroutine(def, args, mov)) + } + + /// Create a new closure type. + pub fn new_coroutine_closure(def: CoroutineClosureDef, args: GenericArgs) -> Ty { + Ty::from_rigid_kind(RigidTy::CoroutineClosure(def, args)) + } + + /// Create a new box type that represents `Box`, for the given inner type `T`. + pub fn new_box(inner_ty: Ty) -> Ty { + with(|cx| cx.new_box_ty(inner_ty)) + } + + /// Create a type representing `usize`. + pub fn usize_ty() -> Ty { + Ty::from_rigid_kind(RigidTy::Uint(UintTy::Usize)) + } + + /// Create a type representing `bool`. + pub fn bool_ty() -> Ty { + Ty::from_rigid_kind(RigidTy::Bool) + } + + /// Create a type representing a signed integer. + pub fn signed_ty(inner: IntTy) -> Ty { + Ty::from_rigid_kind(RigidTy::Int(inner)) + } + + /// Create a type representing an unsigned integer. + pub fn unsigned_ty(inner: UintTy) -> Ty { + Ty::from_rigid_kind(RigidTy::Uint(inner)) + } + + /// Get a type layout. + pub fn layout(self) -> Result { + with(|cx| cx.ty_layout(self)) + } +} + +impl Ty { + pub fn kind(&self) -> TyKind { + with(|context| context.ty_kind(*self)) + } +} + +/// Represents a pattern in the type system +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Pattern { + Range { start: Option, end: Option, include_end: bool }, +} + +/// Represents a constant in the type system +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct TyConst { + pub(crate) kind: TyConstKind, + pub id: TyConstId, +} + +impl TyConst { + pub fn new(kind: TyConstKind, id: TyConstId) -> TyConst { + Self { kind, id } + } + + /// Retrieve the constant kind. + pub fn kind(&self) -> &TyConstKind { + &self.kind + } + + /// Creates an interned usize constant. + pub fn try_from_target_usize(val: u64) -> Result { + with(|cx| cx.try_new_ty_const_uint(val.into(), UintTy::Usize)) + } + + /// Try to evaluate to a target `usize`. + pub fn eval_target_usize(&self) -> Result { + with(|cx| cx.eval_target_usize_ty(self)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum TyConstKind { + Param(ParamConst), + Bound(DebruijnIndex, BoundVar), + Unevaluated(ConstDef, GenericArgs), + + // FIXME: These should be a valtree + Value(Ty, Allocation), + ZSTValue(Ty), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub struct TyConstId(usize); + +/// Represents a constant in MIR +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct MirConst { + /// The constant kind. + pub(crate) kind: ConstantKind, + /// The constant type. + pub(crate) ty: Ty, + /// Used for internal tracking of the internal constant. + pub id: MirConstId, +} + +impl MirConst { + /// Build a constant. Note that this should only be used by the compiler. + pub fn new(kind: ConstantKind, ty: Ty, id: MirConstId) -> MirConst { + MirConst { kind, ty, id } + } + + /// Retrieve the constant kind. + pub fn kind(&self) -> &ConstantKind { + &self.kind + } + + /// Get the constant type. + pub fn ty(&self) -> Ty { + self.ty + } + + /// Try to evaluate to a target `usize`. + pub fn eval_target_usize(&self) -> Result { + with(|cx| cx.eval_target_usize(self)) + } + + /// Create a constant that represents a new zero-sized constant of type T. + /// Fails if the type is not a ZST or if it doesn't have a known size. + pub fn try_new_zero_sized(ty: Ty) -> Result { + with(|cx| cx.try_new_const_zst(ty)) + } + + /// Build a new constant that represents the given string. + /// + /// Note that there is no guarantee today about duplication of the same constant. + /// I.e.: Calling this function multiple times with the same argument may or may not return + /// the same allocation. + pub fn from_str(value: &str) -> MirConst { + with(|cx| cx.new_const_str(value)) + } + + /// Build a new constant that represents the given boolean value. + pub fn from_bool(value: bool) -> MirConst { + with(|cx| cx.new_const_bool(value)) + } + + /// Build a new constant that represents the given unsigned integer. + pub fn try_from_uint(value: u128, uint_ty: UintTy) -> Result { + with(|cx| cx.try_new_const_uint(value, uint_ty)) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +pub struct MirConstId(usize); + +type Ident = Opaque; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct Region { + pub kind: RegionKind, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum RegionKind { + ReEarlyParam(EarlyParamRegion), + ReBound(DebruijnIndex, BoundRegion), + ReStatic, + RePlaceholder(Placeholder), + ReErased, +} + +pub(crate) type DebruijnIndex = u32; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct EarlyParamRegion { + pub index: u32, + pub name: Symbol, +} + +pub(crate) type BoundVar = u32; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct BoundRegion { + pub var: BoundVar, + pub kind: BoundRegionKind, +} + +pub(crate) type UniverseIndex = u32; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct Placeholder { + pub universe: UniverseIndex, + pub bound: T, +} + +#[derive(Clone, Copy, PartialEq, Eq, Serialize)] +pub struct Span(usize); + +impl Debug for Span { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Span") + .field("id", &self.0) + .field("repr", &with(|cx| cx.span_to_string(*self))) + .finish() + } +} + +impl Span { + /// Return filename for diagnostic purposes + pub fn get_filename(&self) -> Filename { + with(|c| c.get_filename(self)) + } + + /// Return lines that correspond to this `Span` + pub fn get_lines(&self) -> LineInfo { + with(|c| c.get_lines(self)) + } + + /// Return the span location to be printed in diagnostic messages. + /// + /// This may leak local file paths and should not be used to build artifacts that may be + /// distributed. + pub fn diagnostic(&self) -> String { + with(|c| c.span_to_string(*self)) + } +} + +#[derive(Clone, Copy, Debug, Serialize)] +/// Information you get from `Span` in a struct form. +/// Line and col start from 1. +pub struct LineInfo { + pub start_line: usize, + pub start_col: usize, + pub end_line: usize, + pub end_col: usize, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum TyKind { + RigidTy(RigidTy), + Alias(AliasKind, AliasTy), + Param(ParamTy), + Bound(usize, BoundTy), +} + +impl TyKind { + pub fn rigid(&self) -> Option<&RigidTy> { + if let TyKind::RigidTy(inner) = self { Some(inner) } else { None } + } + + #[inline] + pub fn is_unit(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Tuple(data)) if data.is_empty()) + } + + #[inline] + pub fn is_bool(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Bool)) + } + + #[inline] + pub fn is_char(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Char)) + } + + #[inline] + pub fn is_trait(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _, DynKind::Dyn))) + } + + #[inline] + pub fn is_enum(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Enum) + } + + #[inline] + pub fn is_struct(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Struct) + } + + #[inline] + pub fn is_union(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Union) + } + + #[inline] + pub fn is_adt(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(..))) + } + + #[inline] + pub fn is_ref(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Ref(..))) + } + + #[inline] + pub fn is_fn(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::FnDef(..))) + } + + #[inline] + pub fn is_fn_ptr(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::FnPtr(..))) + } + + #[inline] + pub fn is_primitive(&self) -> bool { + matches!( + self, + TyKind::RigidTy( + RigidTy::Bool + | RigidTy::Char + | RigidTy::Int(_) + | RigidTy::Uint(_) + | RigidTy::Float(_) + ) + ) + } + + #[inline] + pub fn is_float(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Float(_))) + } + + #[inline] + pub fn is_integral(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Int(_) | RigidTy::Uint(_))) + } + + #[inline] + pub fn is_numeric(&self) -> bool { + self.is_integral() || self.is_float() + } + + #[inline] + pub fn is_signed(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Int(_))) + } + + #[inline] + pub fn is_str(&self) -> bool { + *self == TyKind::RigidTy(RigidTy::Str) + } + + #[inline] + pub fn is_cstr(&self) -> bool { + let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else { + return false; + }; + with(|cx| cx.adt_is_cstr(*def)) + } + + #[inline] + pub fn is_slice(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Slice(_))) + } + + #[inline] + pub fn is_array(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Array(..))) + } + + #[inline] + pub fn is_mutable_ptr(&self) -> bool { + matches!( + self, + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) + | TyKind::RigidTy(RigidTy::Ref(_, _, Mutability::Mut)) + ) + } + + #[inline] + pub fn is_raw_ptr(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::RawPtr(..))) + } + + /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). + #[inline] + pub fn is_any_ptr(&self) -> bool { + self.is_ref() || self.is_raw_ptr() || self.is_fn_ptr() + } + + #[inline] + pub fn is_coroutine(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Coroutine(..))) + } + + #[inline] + pub fn is_closure(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Closure(..))) + } + + #[inline] + pub fn is_box(&self) -> bool { + match self { + TyKind::RigidTy(RigidTy::Adt(def, _)) => def.is_box(), + _ => false, + } + } + + #[inline] + pub fn is_simd(&self) -> bool { + matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.is_simd()) + } + + pub fn trait_principal(&self) -> Option> { + if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _, _)) = self { + if let Some(Binder { value: ExistentialPredicate::Trait(trait_ref), bound_vars }) = + predicates.first() + { + Some(Binder { value: trait_ref.clone(), bound_vars: bound_vars.clone() }) + } else { + None + } + } else { + None + } + } + + /// Returns the type of `ty[i]` for builtin types. + pub fn builtin_index(&self) -> Option { + match self.rigid()? { + RigidTy::Array(ty, _) | RigidTy::Slice(ty) => Some(*ty), + _ => None, + } + } + + /// Returns the type and mutability of `*ty` for builtin types. + /// + /// The parameter `explicit` indicates if this is an *explicit* dereference. + /// Some types -- notably raw ptrs -- can only be dereferenced explicitly. + pub fn builtin_deref(&self, explicit: bool) -> Option { + match self.rigid()? { + RigidTy::Adt(def, args) if def.is_box() => { + Some(TypeAndMut { ty: *args.0.first()?.ty()?, mutability: Mutability::Not }) + } + RigidTy::Ref(_, ty, mutability) => { + Some(TypeAndMut { ty: *ty, mutability: *mutability }) + } + RigidTy::RawPtr(ty, mutability) if explicit => { + Some(TypeAndMut { ty: *ty, mutability: *mutability }) + } + _ => None, + } + } + + /// Get the function signature for function like types (Fn, FnPtr, and Closure) + pub fn fn_sig(&self) -> Option { + match self { + TyKind::RigidTy(RigidTy::FnDef(def, args)) => Some(with(|cx| cx.fn_sig(*def, args))), + TyKind::RigidTy(RigidTy::FnPtr(sig)) => Some(sig.clone()), + TyKind::RigidTy(RigidTy::Closure(_def, args)) => Some(with(|cx| cx.closure_sig(args))), + _ => None, + } + } + + /// Get the discriminant type for this type. + pub fn discriminant_ty(&self) -> Option { + self.rigid().map(|ty| with(|cx| cx.rigid_ty_discriminant_ty(ty))) + } + + /// Deconstruct a function type if this is one. + pub fn fn_def(&self) -> Option<(FnDef, &GenericArgs)> { + if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = self { + Some((*def, args)) + } else { + None + } + } +} + +pub struct TypeAndMut { + pub ty: Ty, + pub mutability: Mutability, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum RigidTy { + Bool, + Char, + Int(IntTy), + Uint(UintTy), + Float(FloatTy), + Adt(AdtDef, GenericArgs), + Foreign(ForeignDef), + Str, + Array(Ty, TyConst), + Pat(Ty, Pattern), + Slice(Ty), + RawPtr(Ty, Mutability), + Ref(Region, Ty, Mutability), + FnDef(FnDef, GenericArgs), + FnPtr(PolyFnSig), + Closure(ClosureDef, GenericArgs), + // FIXME(stable_mir): Movability here is redundant + Coroutine(CoroutineDef, GenericArgs, Movability), + CoroutineClosure(CoroutineClosureDef, GenericArgs), + Dynamic(Vec>, Region, DynKind), + Never, + Tuple(Vec), + CoroutineWitness(CoroutineWitnessDef, GenericArgs), +} + +impl RigidTy { + /// Get the discriminant type for this type. + pub fn discriminant_ty(&self) -> Ty { + with(|cx| cx.rigid_ty_discriminant_ty(self)) + } +} + +impl From for TyKind { + fn from(value: RigidTy) -> Self { + TyKind::RigidTy(value) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +pub enum IntTy { + Isize, + I8, + I16, + I32, + I64, + I128, +} + +impl IntTy { + pub fn num_bytes(self) -> usize { + match self { + IntTy::Isize => MachineInfo::target_pointer_width().bytes(), + IntTy::I8 => 1, + IntTy::I16 => 2, + IntTy::I32 => 4, + IntTy::I64 => 8, + IntTy::I128 => 16, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +pub enum UintTy { + Usize, + U8, + U16, + U32, + U64, + U128, +} + +impl UintTy { + pub fn num_bytes(self) -> usize { + match self { + UintTy::Usize => MachineInfo::target_pointer_width().bytes(), + UintTy::U8 => 1, + UintTy::U16 => 2, + UintTy::U32 => 4, + UintTy::U64 => 8, + UintTy::U128 => 16, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +pub enum FloatTy { + F16, + F32, + F64, + F128, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +pub enum Movability { + Static, + Movable, +} + +crate_def! { + #[derive(Serialize)] + pub ForeignModuleDef; +} + +impl ForeignModuleDef { + pub fn module(&self) -> ForeignModule { + with(|cx| cx.foreign_module(*self)) + } +} + +pub struct ForeignModule { + pub def_id: ForeignModuleDef, + pub abi: Abi, +} + +impl ForeignModule { + pub fn items(&self) -> Vec { + with(|cx| cx.foreign_items(self.def_id)) + } +} + +crate_def_with_ty! { + /// Hold information about a ForeignItem in a crate. + #[derive(Serialize)] + pub ForeignDef; +} + +impl ForeignDef { + pub fn kind(&self) -> ForeignItemKind { + with(|cx| cx.foreign_item_kind(*self)) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] +pub enum ForeignItemKind { + Fn(FnDef), + Static(StaticDef), + Type(Ty), +} + +crate_def_with_ty! { + /// Hold information about a function definition in a crate. + #[derive(Serialize)] + pub FnDef; +} + +impl FnDef { + // Get the function body if available. + pub fn body(&self) -> Option { + with(|ctx| ctx.has_body(self.0).then(|| ctx.mir_body(self.0))) + } + + // Check if the function body is available. + pub fn has_body(&self) -> bool { + with(|ctx| ctx.has_body(self.0)) + } + + /// Get the information of the intrinsic if this function is a definition of one. + pub fn as_intrinsic(&self) -> Option { + with(|cx| cx.intrinsic(self.def_id())) + } + + /// Check if the function is an intrinsic. + #[inline] + pub fn is_intrinsic(&self) -> bool { + self.as_intrinsic().is_some() + } + + /// Get the function signature for this function definition. + pub fn fn_sig(&self) -> PolyFnSig { + let kind = self.ty().kind(); + kind.fn_sig().unwrap() + } +} + +crate_def_with_ty! { + #[derive(Serialize)] + pub IntrinsicDef; +} + +impl IntrinsicDef { + /// Returns the plain name of the intrinsic. + /// e.g., `transmute` for `core::intrinsics::transmute`. + pub fn fn_name(&self) -> Symbol { + with(|cx| cx.intrinsic_name(*self)) + } + + /// Returns whether the intrinsic has no meaningful body and all backends + /// need to shim all calls to it. + pub fn must_be_overridden(&self) -> bool { + with(|cx| !cx.has_body(self.0)) + } +} + +impl From for FnDef { + fn from(def: IntrinsicDef) -> Self { + FnDef(def.0) + } +} + +crate_def! { + #[derive(Serialize)] + pub ClosureDef; +} + +crate_def! { + #[derive(Serialize)] + pub CoroutineDef; +} + +crate_def! { + #[derive(Serialize)] + pub CoroutineClosureDef; +} + +crate_def! { + #[derive(Serialize)] + pub ParamDef; +} + +crate_def! { + #[derive(Serialize)] + pub BrNamedDef; +} + +crate_def! { + #[derive(Serialize)] + pub AdtDef; +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] +pub enum AdtKind { + Enum, + Union, + Struct, +} + +impl AdtDef { + pub fn kind(&self) -> AdtKind { + with(|cx| cx.adt_kind(*self)) + } + + /// Retrieve the type of this Adt. + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.0)) + } + + /// Retrieve the type of this Adt by instantiating and normalizing it with the given arguments. + /// + /// This will assume the type can be instantiated with these arguments. + pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { + with(|cx| cx.def_ty_with_args(self.0, args)) + } + + pub fn is_box(&self) -> bool { + with(|cx| cx.adt_is_box(*self)) + } + + pub fn is_simd(&self) -> bool { + with(|cx| cx.adt_is_simd(*self)) + } + + /// The number of variants in this ADT. + pub fn num_variants(&self) -> usize { + with(|cx| cx.adt_variants_len(*self)) + } + + /// Retrieve the variants in this ADT. + pub fn variants(&self) -> Vec { + self.variants_iter().collect() + } + + /// Iterate over the variants in this ADT. + pub fn variants_iter(&self) -> impl Iterator { + (0..self.num_variants()) + .map(|idx| VariantDef { idx: VariantIdx::to_val(idx), adt_def: *self }) + } + + pub fn variant(&self, idx: VariantIdx) -> Option { + (idx.to_index() < self.num_variants()).then_some(VariantDef { idx, adt_def: *self }) + } +} + +/// Definition of a variant, which can be either a struct / union field or an enum variant. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct VariantDef { + /// The variant index. + /// + /// ## Warning + /// Do not access this field directly! + pub idx: VariantIdx, + /// The data type where this variant comes from. + /// For now, we use this to retrieve information about the variant itself so we don't need to + /// cache more information. + /// + /// ## Warning + /// Do not access this field directly! + pub adt_def: AdtDef, +} + +impl VariantDef { + pub fn name(&self) -> Symbol { + with(|cx| cx.variant_name(*self)) + } + + /// Retrieve all the fields in this variant. + // We expect user to cache this and use it directly since today it is expensive to generate all + // fields name. + pub fn fields(&self) -> Vec { + with(|cx| cx.variant_fields(*self)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct FieldDef { + /// The field definition. + /// + /// ## Warning + /// Do not access this field directly! This is public for the compiler to have access to it. + pub def: DefId, + + /// The field name. + pub name: Symbol, +} + +impl FieldDef { + /// Retrieve the type of this field instantiating and normalizing it with the given arguments. + /// + /// This will assume the type can be instantiated with these arguments. + pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { + with(|cx| cx.def_ty_with_args(self.def, args)) + } + + /// Retrieve the type of this field. + pub fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.def)) + } +} + +impl Display for AdtKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match self { + AdtKind::Enum => "enum", + AdtKind::Union => "union", + AdtKind::Struct => "struct", + }) + } +} + +impl AdtKind { + pub fn is_enum(&self) -> bool { + matches!(self, AdtKind::Enum) + } + + pub fn is_struct(&self) -> bool { + matches!(self, AdtKind::Struct) + } + + pub fn is_union(&self) -> bool { + matches!(self, AdtKind::Union) + } +} + +crate_def! { + #[derive(Serialize)] + pub AliasDef; +} + +crate_def! { + /// A trait's definition. + #[derive(Serialize)] + pub TraitDef; +} + +impl_crate_def_items! { + TraitDef; +} + +impl TraitDef { + pub fn declaration(trait_def: &TraitDef) -> TraitDecl { + with(|cx| cx.trait_decl(trait_def)) + } +} + +crate_def! { + #[derive(Serialize)] + pub GenericDef; +} + +crate_def_with_ty! { + #[derive(Serialize)] + pub ConstDef; +} + +crate_def! { + /// A trait impl definition. + #[derive(Serialize)] + pub ImplDef; +} + +impl_crate_def_items! { + ImplDef; +} + +impl ImplDef { + /// Retrieve information about this implementation. + pub fn trait_impl(&self) -> ImplTrait { + with(|cx| cx.trait_impl(self)) + } +} + +crate_def! { + #[derive(Serialize)] + pub RegionDef; +} + +crate_def! { + #[derive(Serialize)] + pub CoroutineWitnessDef; +} + +/// A list of generic arguments. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct GenericArgs(pub Vec); + +impl std::ops::Index for GenericArgs { + type Output = Ty; + + fn index(&self, index: ParamTy) -> &Self::Output { + self.0[index.index as usize].expect_ty() + } +} + +impl std::ops::Index for GenericArgs { + type Output = TyConst; + + fn index(&self, index: ParamConst) -> &Self::Output { + self.0[index.index as usize].expect_const() + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum GenericArgKind { + Lifetime(Region), + Type(Ty), + Const(TyConst), +} + +impl GenericArgKind { + /// Panic if this generic argument is not a type, otherwise + /// return the type. + #[track_caller] + pub fn expect_ty(&self) -> &Ty { + match self { + GenericArgKind::Type(ty) => ty, + _ => panic!("{self:?}"), + } + } + + /// Panic if this generic argument is not a const, otherwise + /// return the const. + #[track_caller] + pub fn expect_const(&self) -> &TyConst { + match self { + GenericArgKind::Const(c) => c, + _ => panic!("{self:?}"), + } + } + + /// Return the generic argument type if applicable, otherwise return `None`. + pub fn ty(&self) -> Option<&Ty> { + match self { + GenericArgKind::Type(ty) => Some(ty), + _ => None, + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum TermKind { + Type(Ty), + Const(TyConst), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AliasKind { + Projection, + Inherent, + Opaque, + Weak, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct AliasTy { + pub def_id: AliasDef, + pub args: GenericArgs, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct AliasTerm { + pub def_id: AliasDef, + pub args: GenericArgs, +} + +pub type PolyFnSig = Binder; + +impl PolyFnSig { + /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. + /// + /// NB: this doesn't handle virtual calls - those should use `Instance::fn_abi` + /// instead, where the instance is an `InstanceKind::Virtual`. + pub fn fn_ptr_abi(self) -> Result { + with(|cx| cx.fn_ptr_abi(self)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct FnSig { + pub inputs_and_output: Vec, + pub c_variadic: bool, + pub safety: Safety, + pub abi: Abi, +} + +impl FnSig { + pub fn output(&self) -> Ty { + self.inputs_and_output[self.inputs_and_output.len() - 1] + } + + pub fn inputs(&self) -> &[Ty] { + &self.inputs_and_output[..self.inputs_and_output.len() - 1] + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] +pub enum Abi { + Rust, + C { unwind: bool }, + Cdecl { unwind: bool }, + Stdcall { unwind: bool }, + Fastcall { unwind: bool }, + Vectorcall { unwind: bool }, + Thiscall { unwind: bool }, + Aapcs { unwind: bool }, + Win64 { unwind: bool }, + SysV64 { unwind: bool }, + PtxKernel, + Msp430Interrupt, + X86Interrupt, + GpuKernel, + EfiApi, + AvrInterrupt, + AvrNonBlockingInterrupt, + CCmseNonSecureCall, + CCmseNonSecureEntry, + System { unwind: bool }, + RustIntrinsic, + RustCall, + Unadjusted, + RustCold, + RiscvInterruptM, + RiscvInterruptS, +} + +/// A binder represents a possibly generic type and its bound vars. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct Binder { + pub value: T, + pub bound_vars: Vec, +} + +impl Binder { + /// Create a new binder with the given bound vars. + pub fn bind_with_vars(value: T, bound_vars: Vec) -> Self { + Binder { value, bound_vars } + } + + /// Create a new binder with no bounded variable. + pub fn dummy(value: T) -> Self { + Binder { value, bound_vars: vec![] } + } + + pub fn skip_binder(self) -> T { + self.value + } + + pub fn map_bound_ref(&self, f: F) -> Binder + where + F: FnOnce(&T) -> U, + { + let Binder { value, bound_vars } = self; + let new_value = f(value); + Binder { value: new_value, bound_vars: bound_vars.clone() } + } + + pub fn map_bound(self, f: F) -> Binder + where + F: FnOnce(T) -> U, + { + let Binder { value, bound_vars } = self; + let new_value = f(value); + Binder { value: new_value, bound_vars } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct EarlyBinder { + pub value: T, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum BoundVariableKind { + Ty(BoundTyKind), + Region(BoundRegionKind), + Const, +} + +#[derive(Clone, PartialEq, Eq, Debug, Serialize)] +pub enum BoundTyKind { + Anon, + Param(ParamDef, String), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum BoundRegionKind { + BrAnon, + BrNamed(BrNamedDef, String), + BrEnv, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum DynKind { + Dyn, + DynStar, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum ExistentialPredicate { + Trait(ExistentialTraitRef), + Projection(ExistentialProjection), + AutoTrait(TraitDef), +} + +/// An existential reference to a trait where `Self` is not included. +/// +/// The `generic_args` will include any other known argument. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ExistentialTraitRef { + pub def_id: TraitDef, + pub generic_args: GenericArgs, +} + +impl Binder { + pub fn with_self_ty(&self, self_ty: Ty) -> Binder { + self.map_bound_ref(|trait_ref| trait_ref.with_self_ty(self_ty)) + } +} + +impl ExistentialTraitRef { + pub fn with_self_ty(&self, self_ty: Ty) -> TraitRef { + TraitRef::new(self.def_id, self_ty, &self.generic_args) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ExistentialProjection { + pub def_id: TraitDef, + pub generic_args: GenericArgs, + pub term: TermKind, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ParamTy { + pub index: u32, + pub name: String, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct BoundTy { + pub var: usize, + pub kind: BoundTyKind, +} + +pub type Bytes = Vec>; + +/// Size in bytes. +pub type Size = usize; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] +pub struct Prov(pub AllocId); + +pub type Align = u64; +pub type Promoted = u32; +pub type InitMaskMaterialized = Vec; + +/// Stores the provenance information of pointers stored in memory. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)] +pub struct ProvenanceMap { + /// Provenance in this map applies from the given offset for an entire pointer-size worth of + /// bytes. Two entries in this map are always at least a pointer size apart. + pub ptrs: Vec<(Size, Prov)>, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)] +pub struct Allocation { + pub bytes: Bytes, + pub provenance: ProvenanceMap, + pub align: Align, + pub mutability: Mutability, +} + +impl Allocation { + /// Get a vector of bytes for an Allocation that has been fully initialized + pub fn raw_bytes(&self) -> Result, Error> { + self.bytes + .iter() + .copied() + .collect::>>() + .ok_or_else(|| error!("Found uninitialized bytes: `{:?}`", self.bytes)) + } + + /// Read a uint value from the specified range. + pub fn read_partial_uint(&self, range: Range) -> Result { + if range.end - range.start > 16 { + return Err(error!("Allocation is bigger than largest integer")); + } + if range.end > self.bytes.len() { + return Err(error!( + "Range is out of bounds. Allocation length is `{}`, but requested range `{:?}`", + self.bytes.len(), + range + )); + } + let raw = self.bytes[range] + .iter() + .copied() + .collect::>>() + .ok_or_else(|| error!("Found uninitialized bytes: `{:?}`", self.bytes))?; + read_target_uint(&raw) + } + + /// Read this allocation and try to convert it to an unassigned integer. + pub fn read_uint(&self) -> Result { + if self.bytes.len() > 16 { + return Err(error!("Allocation is bigger than largest integer")); + } + let raw = self.raw_bytes()?; + read_target_uint(&raw) + } + + /// Read this allocation and try to convert it to a signed integer. + pub fn read_int(&self) -> Result { + if self.bytes.len() > 16 { + return Err(error!("Allocation is bigger than largest integer")); + } + let raw = self.raw_bytes()?; + read_target_int(&raw) + } + + /// Read this allocation and try to convert it to a boolean. + pub fn read_bool(&self) -> Result { + match self.read_int()? { + 0 => Ok(false), + 1 => Ok(true), + val => Err(error!("Unexpected value for bool: `{val}`")), + } + } + + /// Read this allocation as a pointer and return whether it represents a `null` pointer. + pub fn is_null(&self) -> Result { + let len = self.bytes.len(); + let ptr_len = MachineInfo::target_pointer_width().bytes(); + if len != ptr_len { + return Err(error!("Expected width of pointer (`{ptr_len}`), but found: `{len}`")); + } + Ok(self.read_uint()? == 0 && self.provenance.ptrs.is_empty()) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum ConstantKind { + Ty(TyConst), + Allocated(Allocation), + Unevaluated(UnevaluatedConst), + Param(ParamConst), + /// Store ZST constants. + /// We have to special handle these constants since its type might be generic. + ZeroSized, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ParamConst { + pub index: u32, + pub name: String, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct UnevaluatedConst { + pub def: ConstDef, + pub args: GenericArgs, + pub promoted: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +pub enum TraitSpecializationKind { + None, + Marker, + AlwaysApplicable, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct TraitDecl { + pub def_id: TraitDef, + pub safety: Safety, + pub paren_sugar: bool, + pub has_auto_impl: bool, + pub is_marker: bool, + pub is_coinductive: bool, + pub skip_array_during_method_dispatch: bool, + pub skip_boxed_slice_during_method_dispatch: bool, + pub specialization_kind: TraitSpecializationKind, + pub must_implement_one_of: Option>, + pub implement_via_object: bool, + pub deny_explicit_impl: bool, +} + +impl TraitDecl { + pub fn generics_of(&self) -> Generics { + with(|cx| cx.generics_of(self.def_id.0)) + } + + pub fn predicates_of(&self) -> GenericPredicates { + with(|cx| cx.predicates_of(self.def_id.0)) + } + + pub fn explicit_predicates_of(&self) -> GenericPredicates { + with(|cx| cx.explicit_predicates_of(self.def_id.0)) + } +} + +pub type ImplTrait = EarlyBinder; + +/// A complete reference to a trait, i.e., one where `Self` is known. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct TraitRef { + pub def_id: TraitDef, + /// The generic arguments for this definition. + /// The first element must always be type, and it represents `Self`. + args: GenericArgs, +} + +impl TraitRef { + pub fn new(def_id: TraitDef, self_ty: Ty, gen_args: &GenericArgs) -> TraitRef { + let mut args = vec![GenericArgKind::Type(self_ty)]; + args.extend_from_slice(&gen_args.0); + TraitRef { def_id, args: GenericArgs(args) } + } + + pub fn try_new(def_id: TraitDef, args: GenericArgs) -> Result { + match &args.0[..] { + [GenericArgKind::Type(_), ..] => Ok(TraitRef { def_id, args }), + _ => Err(()), + } + } + + pub fn args(&self) -> &GenericArgs { + &self.args + } + + pub fn self_ty(&self) -> Ty { + let GenericArgKind::Type(self_ty) = self.args.0[0] else { + panic!("Self must be a type, but found: {:?}", self.args.0[0]) + }; + self_ty + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct Generics { + pub parent: Option, + pub parent_count: usize, + pub params: Vec, + pub param_def_id_to_index: Vec<(GenericDef, u32)>, + pub has_self: bool, + pub has_late_bound_regions: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum GenericParamDefKind { + Lifetime, + Type { has_default: bool, synthetic: bool }, + Const { has_default: bool }, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct GenericParamDef { + pub name: super::Symbol, + pub def_id: GenericDef, + pub index: u32, + pub pure_wrt_drop: bool, + pub kind: GenericParamDefKind, +} + +pub struct GenericPredicates { + pub parent: Option, + pub predicates: Vec<(PredicateKind, Span)>, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum PredicateKind { + Clause(ClauseKind), + DynCompatible(TraitDef), + SubType(SubtypePredicate), + Coerce(CoercePredicate), + ConstEquate(TyConst, TyConst), + Ambiguous, + AliasRelate(TermKind, TermKind, AliasRelationDirection), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum ClauseKind { + Trait(TraitPredicate), + RegionOutlives(RegionOutlivesPredicate), + TypeOutlives(TypeOutlivesPredicate), + Projection(ProjectionPredicate), + ConstArgHasType(TyConst, Ty), + WellFormed(GenericArgKind), + ConstEvaluatable(TyConst), +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum ClosureKind { + Fn, + FnMut, + FnOnce, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct SubtypePredicate { + pub a: Ty, + pub b: Ty, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct CoercePredicate { + pub a: Ty, + pub b: Ty, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AliasRelationDirection { + Equate, + Subtype, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct TraitPredicate { + pub trait_ref: TraitRef, + pub polarity: PredicatePolarity, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct OutlivesPredicate(pub A, pub B); + +pub type RegionOutlivesPredicate = OutlivesPredicate; +pub type TypeOutlivesPredicate = OutlivesPredicate; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct ProjectionPredicate { + pub projection_term: AliasTerm, + pub term: TermKind, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum ImplPolarity { + Positive, + Negative, + Reservation, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum PredicatePolarity { + Positive, + Negative, +} + +pub trait IndexedVal { + fn to_val(index: usize) -> Self; + + fn to_index(&self) -> usize; +} + +macro_rules! index_impl { + ($name:ident) => { + impl IndexedVal for $name { + fn to_val(index: usize) -> Self { + $name(index) + } + fn to_index(&self) -> usize { + self.0 + } + } + }; +} + +index_impl!(TyConstId); +index_impl!(MirConstId); +index_impl!(Ty); +index_impl!(Span); + +/// The source-order index of a variant in a type. +/// +/// For example, in the following types, +/// ```ignore(illustrative) +/// enum Demo1 { +/// Variant0 { a: bool, b: i32 }, +/// Variant1 { c: u8, d: u64 }, +/// } +/// struct Demo2 { e: u8, f: u16, g: u8 } +/// ``` +/// `a` is in the variant with the `VariantIdx` of `0`, +/// `c` is in the variant with the `VariantIdx` of `1`, and +/// `g` is in the variant with the `VariantIdx` of `0`. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct VariantIdx(usize); + +index_impl!(VariantIdx); + +crate_def! { + /// Hold infomation about an Opaque definition, particularly useful in `RPITIT`. + #[derive(Serialize)] + pub OpaqueDef; +} + +crate_def! { + #[derive(Serialize)] + pub AssocDef; +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub struct AssocItem { + pub def_id: AssocDef, + pub name: Symbol, + pub kind: AssocKind, + pub container: AssocItemContainer, + + /// If this is an item in an impl of a trait then this is the `DefId` of + /// the associated item on the trait that this implements. + pub trait_item_def_id: Option, + + /// Whether this is a method with an explicit self + /// as its first parameter, allowing method calls. + pub fn_has_self_parameter: bool, + + /// `Some` if the associated item (an associated type) comes from the + /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData` + /// provides additional information about its source. + pub opt_rpitit_info: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AssocKind { + Const, + Fn, + Type, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum AssocItemContainer { + Trait, + Impl, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] +pub enum ImplTraitInTraitData { + Trait { fn_def_id: FnDef, opaque_def_id: OpaqueDef }, + Impl { fn_def_id: FnDef }, +} + +impl AssocItem { + pub fn is_impl_trait_in_trait(&self) -> bool { + self.opt_rpitit_info.is_some() + } +} diff --git a/compiler/rustc_smir/src/stable_mir/visitor.rs b/compiler/rustc_smir/src/stable_mir/visitor.rs new file mode 100644 index 00000000000..31a53d1b19d --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/visitor.rs @@ -0,0 +1,226 @@ +use std::ops::ControlFlow; + +use stable_mir::Opaque; +use stable_mir::ty::TyConst; + +use super::ty::{ + Allocation, Binder, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs, + MirConst, Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst, +}; +use crate::stable_mir; + +pub trait Visitor: Sized { + type Break; + fn visit_ty(&mut self, ty: &Ty) -> ControlFlow { + ty.super_visit(self) + } + fn visit_const(&mut self, c: &TyConst) -> ControlFlow { + c.super_visit(self) + } + fn visit_reg(&mut self, reg: &Region) -> ControlFlow { + reg.super_visit(self) + } +} + +pub trait Visitable { + fn visit(&self, visitor: &mut V) -> ControlFlow { + self.super_visit(visitor) + } + fn super_visit(&self, visitor: &mut V) -> ControlFlow; +} + +impl Visitable for Ty { + fn visit(&self, visitor: &mut V) -> ControlFlow { + visitor.visit_ty(self) + } + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match self.kind() { + super::ty::TyKind::RigidTy(ty) => ty.visit(visitor)?, + super::ty::TyKind::Alias(_, alias) => alias.args.visit(visitor)?, + super::ty::TyKind::Param(_) | super::ty::TyKind::Bound(_, _) => {} + } + ControlFlow::Continue(()) + } +} + +impl Visitable for TyConst { + fn visit(&self, visitor: &mut V) -> ControlFlow { + visitor.visit_const(self) + } + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match &self.kind { + super::ty::TyConstKind::Param(_) | super::ty::TyConstKind::Bound(_, _) => {} + super::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?, + super::ty::TyConstKind::Value(ty, alloc) => { + alloc.visit(visitor)?; + ty.visit(visitor)?; + } + super::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?, + } + ControlFlow::Continue(()) + } +} + +impl Visitable for MirConst { + fn visit(&self, visitor: &mut V) -> ControlFlow { + self.super_visit(visitor) + } + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match &self.kind() { + super::ty::ConstantKind::Ty(ct) => ct.visit(visitor)?, + super::ty::ConstantKind::Allocated(alloc) => alloc.visit(visitor)?, + super::ty::ConstantKind::Unevaluated(uv) => uv.visit(visitor)?, + super::ty::ConstantKind::Param(_) | super::ty::ConstantKind::ZeroSized => {} + } + self.ty().visit(visitor) + } +} + +impl Visitable for Opaque { + fn super_visit(&self, _visitor: &mut V) -> ControlFlow { + ControlFlow::Continue(()) + } +} + +impl Visitable for Allocation { + fn super_visit(&self, _visitor: &mut V) -> ControlFlow { + ControlFlow::Continue(()) + } +} + +impl Visitable for UnevaluatedConst { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + let UnevaluatedConst { def, args, promoted } = self; + def.visit(visitor)?; + args.visit(visitor)?; + promoted.visit(visitor) + } +} + +impl Visitable for ConstDef { + fn super_visit(&self, _visitor: &mut V) -> ControlFlow { + ControlFlow::Continue(()) + } +} + +impl Visitable for Option { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match self { + Some(val) => val.visit(visitor), + None => ControlFlow::Continue(()), + } + } +} + +impl Visitable for Promoted { + fn super_visit(&self, _visitor: &mut V) -> ControlFlow { + ControlFlow::Continue(()) + } +} + +impl Visitable for GenericArgs { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + self.0.visit(visitor) + } +} + +impl Visitable for Region { + fn visit(&self, visitor: &mut V) -> ControlFlow { + visitor.visit_reg(self) + } + + fn super_visit(&self, _: &mut V) -> ControlFlow { + ControlFlow::Continue(()) + } +} + +impl Visitable for GenericArgKind { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match self { + GenericArgKind::Lifetime(lt) => lt.visit(visitor), + GenericArgKind::Type(t) => t.visit(visitor), + GenericArgKind::Const(c) => c.visit(visitor), + } + } +} + +impl Visitable for RigidTy { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match self { + RigidTy::Bool + | RigidTy::Char + | RigidTy::Int(_) + | RigidTy::Uint(_) + | RigidTy::Float(_) + | RigidTy::Never + | RigidTy::Foreign(_) + | RigidTy::Str => ControlFlow::Continue(()), + RigidTy::Array(t, c) => { + t.visit(visitor)?; + c.visit(visitor) + } + RigidTy::Pat(t, _p) => t.visit(visitor), + RigidTy::Slice(inner) => inner.visit(visitor), + RigidTy::RawPtr(ty, _) => ty.visit(visitor), + RigidTy::Ref(reg, ty, _) => { + reg.visit(visitor)?; + ty.visit(visitor) + } + RigidTy::Adt(_, args) + | RigidTy::Closure(_, args) + | RigidTy::Coroutine(_, args, _) + | RigidTy::CoroutineWitness(_, args) + | RigidTy::CoroutineClosure(_, args) + | RigidTy::FnDef(_, args) => args.visit(visitor), + RigidTy::FnPtr(sig) => sig.visit(visitor), + RigidTy::Dynamic(pred, r, _) => { + pred.visit(visitor)?; + r.visit(visitor) + } + RigidTy::Tuple(fields) => fields.visit(visitor), + } + } +} + +impl Visitable for Vec { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + for arg in self { + arg.visit(visitor)?; + } + ControlFlow::Continue(()) + } +} + +impl Visitable for Binder { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + self.value.visit(visitor) + } +} + +impl Visitable for ExistentialPredicate { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match self { + ExistentialPredicate::Trait(tr) => tr.generic_args.visit(visitor), + ExistentialPredicate::Projection(p) => { + p.term.visit(visitor)?; + p.generic_args.visit(visitor) + } + ExistentialPredicate::AutoTrait(_) => ControlFlow::Continue(()), + } + } +} + +impl Visitable for TermKind { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match self { + TermKind::Type(t) => t.visit(visitor), + TermKind::Const(c) => c.visit(visitor), + } + } +} + +impl Visitable for FnSig { + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + self.inputs_and_output.visit(visitor) + } +} diff --git a/compiler/stable_mir/Cargo.toml b/compiler/stable_mir/Cargo.toml index d691a0e4f22..3a01ee5783e 100644 --- a/compiler/stable_mir/Cargo.toml +++ b/compiler/stable_mir/Cargo.toml @@ -4,5 +4,4 @@ version = "0.1.0-preview" edition = "2024" [dependencies] -scoped-tls = "1.0" -serde = { version = "1.0.125", features = [ "derive" ] } +rustc_smir = { path = "../rustc_smir" } diff --git a/compiler/stable_mir/src/abi.rs b/compiler/stable_mir/src/abi.rs deleted file mode 100644 index 091f3e1a95e..00000000000 --- a/compiler/stable_mir/src/abi.rs +++ /dev/null @@ -1,456 +0,0 @@ -use std::fmt::{self, Debug}; -use std::num::NonZero; -use std::ops::RangeInclusive; - -use serde::Serialize; - -use crate::compiler_interface::with; -use crate::mir::FieldIdx; -use crate::target::{MachineInfo, MachineSize as Size}; -use crate::ty::{Align, IndexedVal, Ty, VariantIdx}; -use crate::{Error, Opaque, error}; - -/// A function ABI definition. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct FnAbi { - /// The types of each argument. - pub args: Vec, - - /// The expected return type. - pub ret: ArgAbi, - - /// The count of non-variadic arguments. - /// - /// Should only be different from `args.len()` when a function is a C variadic function. - pub fixed_count: u32, - - /// The ABI convention. - pub conv: CallConvention, - - /// Whether this is a variadic C function, - pub c_variadic: bool, -} - -/// Information about the ABI of a function's argument, or return value. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct ArgAbi { - pub ty: Ty, - pub layout: Layout, - pub mode: PassMode, -} - -/// How a function argument should be passed in to the target function. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum PassMode { - /// Ignore the argument. - /// - /// The argument is either uninhabited or a ZST. - Ignore, - /// Pass the argument directly. - /// - /// The argument has a layout abi of `Scalar` or `Vector`. - Direct(Opaque), - /// Pass a pair's elements directly in two arguments. - /// - /// The argument has a layout abi of `ScalarPair`. - Pair(Opaque, Opaque), - /// Pass the argument after casting it. - Cast { pad_i32: bool, cast: Opaque }, - /// Pass the argument indirectly via a hidden pointer. - Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool }, -} - -/// The layout of a type, alongside the type itself. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct TyAndLayout { - pub ty: Ty, - pub layout: Layout, -} - -/// The layout of a type in memory. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct LayoutShape { - /// The fields location within the layout - pub fields: FieldsShape, - - /// Encodes information about multi-variant layouts. - /// Even with `Multiple` variants, a layout still has its own fields! Those are then - /// shared between all variants. - /// - /// To access all fields of this layout, both `fields` and the fields of the active variant - /// must be taken into account. - pub variants: VariantsShape, - - /// The `abi` defines how this data is passed between functions. - pub abi: ValueAbi, - - /// The ABI mandated alignment in bytes. - pub abi_align: Align, - - /// The size of this layout in bytes. - pub size: Size, -} - -impl LayoutShape { - /// Returns `true` if the layout corresponds to an unsized type. - #[inline] - pub fn is_unsized(&self) -> bool { - self.abi.is_unsized() - } - - #[inline] - pub fn is_sized(&self) -> bool { - !self.abi.is_unsized() - } - - /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1). - pub fn is_1zst(&self) -> bool { - self.is_sized() && self.size.bits() == 0 && self.abi_align == 1 - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct Layout(usize); - -impl Layout { - pub fn shape(self) -> LayoutShape { - with(|cx| cx.layout_shape(self)) - } -} - -impl IndexedVal for Layout { - fn to_val(index: usize) -> Self { - Layout(index) - } - fn to_index(&self) -> usize { - self.0 - } -} - -/// Describes how the fields of a type are shaped in memory. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum FieldsShape { - /// Scalar primitives and `!`, which never have fields. - Primitive, - - /// All fields start at no offset. The `usize` is the field count. - Union(NonZero), - - /// Array/vector-like placement, with all fields of identical types. - Array { stride: Size, count: u64 }, - - /// Struct-like placement, with precomputed offsets. - /// - /// Fields are guaranteed to not overlap, but note that gaps - /// before, between and after all the fields are NOT always - /// padding, and as such their contents may not be discarded. - /// For example, enum variants leave a gap at the start, - /// where the discriminant field in the enum layout goes. - Arbitrary { - /// Offsets for the first byte of each field, - /// ordered to match the source definition order. - /// I.e.: It follows the same order as [crate::ty::VariantDef::fields()]. - /// This vector does not go in increasing order. - offsets: Vec, - }, -} - -impl FieldsShape { - pub fn fields_by_offset_order(&self) -> Vec { - match self { - FieldsShape::Primitive => vec![], - FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(), - FieldsShape::Arbitrary { offsets, .. } => { - let mut indices = (0..offsets.len()).collect::>(); - indices.sort_by_key(|idx| offsets[*idx]); - indices - } - } - } - - pub fn count(&self) -> usize { - match self { - FieldsShape::Primitive => 0, - FieldsShape::Union(count) => count.get(), - FieldsShape::Array { count, .. } => *count as usize, - FieldsShape::Arbitrary { offsets, .. } => offsets.len(), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum VariantsShape { - /// A type with no valid variants. Must be uninhabited. - Empty, - - /// Single enum variants, structs/tuples, unions, and all non-ADTs. - Single { index: VariantIdx }, - - /// Enum-likes with more than one inhabited variant: each variant comes with - /// a *discriminant* (usually the same as the variant index but the user can - /// assign explicit discriminant values). That discriminant is encoded - /// as a *tag* on the machine. The layout of each variant is - /// a struct, and they all have space reserved for the tag. - /// For enums, the tag is the sole field of the layout. - Multiple { - tag: Scalar, - tag_encoding: TagEncoding, - tag_field: usize, - variants: Vec, - }, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum TagEncoding { - /// The tag directly stores the discriminant, but possibly with a smaller layout - /// (so converting the tag to the discriminant can require sign extension). - Direct, - - /// Niche (values invalid for a type) encoding the discriminant: - /// Discriminant and variant index coincide. - /// The variant `untagged_variant` contains a niche at an arbitrary - /// offset (field `tag_field` of the enum), which for a variant with - /// discriminant `d` is set to - /// `(d - niche_variants.start).wrapping_add(niche_start)`. - /// - /// For example, `Option<(usize, &T)>` is represented such that - /// `None` has a null pointer for the second tuple field, and - /// `Some` is the identity function (with a non-null reference). - Niche { - untagged_variant: VariantIdx, - niche_variants: RangeInclusive, - niche_start: u128, - }, -} - -/// Describes how values of the type are passed by target ABIs, -/// in terms of categories of C types there are ABI rules for. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum ValueAbi { - Scalar(Scalar), - ScalarPair(Scalar, Scalar), - Vector { - element: Scalar, - count: u64, - }, - Aggregate { - /// If true, the size is exact, otherwise it's only a lower bound. - sized: bool, - }, -} - -impl ValueAbi { - /// Returns `true` if the layout corresponds to an unsized type. - pub fn is_unsized(&self) -> bool { - match *self { - ValueAbi::Scalar(_) | ValueAbi::ScalarPair(..) | ValueAbi::Vector { .. } => false, - ValueAbi::Aggregate { sized } => !sized, - } - } -} - -/// Information about one scalar component of a Rust type. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize)] -pub enum Scalar { - Initialized { - /// The primitive type used to represent this value. - value: Primitive, - /// The range that represents valid values. - /// The range must be valid for the `primitive` size. - valid_range: WrappingRange, - }, - Union { - /// Unions never have niches, so there is no `valid_range`. - /// Even for unions, we need to use the correct registers for the kind of - /// values inside the union, so we keep the `Primitive` type around. - /// It is also used to compute the size of the scalar. - value: Primitive, - }, -} - -impl Scalar { - pub fn has_niche(&self, target: &MachineInfo) -> bool { - match self { - Scalar::Initialized { value, valid_range } => { - !valid_range.is_full(value.size(target)).unwrap() - } - Scalar::Union { .. } => false, - } - } -} - -/// Fundamental unit of memory access and layout. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)] -pub enum Primitive { - /// The `bool` is the signedness of the `Integer` type. - /// - /// One would think we would not care about such details this low down, - /// but some ABIs are described in terms of C types and ISAs where the - /// integer arithmetic is done on {sign,zero}-extended registers, e.g. - /// a negative integer passed by zero-extension will appear positive in - /// the callee, and most operations on it will produce the wrong values. - Int { - length: IntegerLength, - signed: bool, - }, - Float { - length: FloatLength, - }, - Pointer(AddressSpace), -} - -impl Primitive { - pub fn size(self, target: &MachineInfo) -> Size { - match self { - Primitive::Int { length, .. } => Size::from_bits(length.bits()), - Primitive::Float { length } => Size::from_bits(length.bits()), - Primitive::Pointer(_) => target.pointer_width, - } - } -} - -/// Enum representing the existing integer lengths. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] -pub enum IntegerLength { - I8, - I16, - I32, - I64, - I128, -} - -/// Enum representing the existing float lengths. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] -pub enum FloatLength { - F16, - F32, - F64, - F128, -} - -impl IntegerLength { - pub fn bits(self) -> usize { - match self { - IntegerLength::I8 => 8, - IntegerLength::I16 => 16, - IntegerLength::I32 => 32, - IntegerLength::I64 => 64, - IntegerLength::I128 => 128, - } - } -} - -impl FloatLength { - pub fn bits(self) -> usize { - match self { - FloatLength::F16 => 16, - FloatLength::F32 => 32, - FloatLength::F64 => 64, - FloatLength::F128 => 128, - } - } -} - -/// An identifier that specifies the address space that some operation -/// should operate on. Special address spaces have an effect on code generation, -/// depending on the target and the address spaces it implements. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -pub struct AddressSpace(pub u32); - -impl AddressSpace { - /// The default address space, corresponding to data space. - pub const DATA: Self = AddressSpace(0); -} - -/// Inclusive wrap-around range of valid values (bitwise representation), that is, if -/// start > end, it represents `start..=MAX`, followed by `0..=end`. -/// -/// That is, for an i8 primitive, a range of `254..=2` means following -/// sequence: -/// -/// 254 (-2), 255 (-1), 0, 1, 2 -#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] -pub struct WrappingRange { - pub start: u128, - pub end: u128, -} - -impl WrappingRange { - /// Returns `true` if `size` completely fills the range. - #[inline] - pub fn is_full(&self, size: Size) -> Result { - let Some(max_value) = size.unsigned_int_max() else { - return Err(error!("Expected size <= 128 bits, but found {} instead", size.bits())); - }; - if self.start <= max_value && self.end <= max_value { - Ok(self.start == (self.end.wrapping_add(1) & max_value)) - } else { - Err(error!("Range `{self:?}` out of bounds for size `{}` bits.", size.bits())) - } - } - - /// Returns `true` if `v` is contained in the range. - #[inline(always)] - pub fn contains(&self, v: u128) -> bool { - if self.wraps_around() { - self.start <= v || v <= self.end - } else { - self.start <= v && v <= self.end - } - } - - /// Returns `true` if the range wraps around. - /// I.e., the range represents the union of `self.start..=MAX` and `0..=self.end`. - /// Returns `false` if this is a non-wrapping range, i.e.: `self.start..=self.end`. - #[inline] - pub fn wraps_around(&self) -> bool { - self.start > self.end - } -} - -impl Debug for WrappingRange { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.start > self.end { - write!(fmt, "(..={}) | ({}..)", self.end, self.start)?; - } else { - write!(fmt, "{}..={}", self.start, self.end)?; - } - Ok(()) - } -} - -/// General language calling conventions. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum CallConvention { - C, - Rust, - - Cold, - PreserveMost, - PreserveAll, - - // Target-specific calling conventions. - ArmAapcs, - CCmseNonSecureCall, - CCmseNonSecureEntry, - - Msp430Intr, - - PtxKernel, - - GpuKernel, - - X86Fastcall, - X86Intr, - X86Stdcall, - X86ThisCall, - X86VectorCall, - - X86_64SysV, - X86_64Win64, - - AvrInterrupt, - AvrNonBlockingInterrupt, - - RiscvInterrupt, -} diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs deleted file mode 100644 index 46154da36ca..00000000000 --- a/compiler/stable_mir/src/compiler_interface.rs +++ /dev/null @@ -1,286 +0,0 @@ -//! Define the interface with the Rust compiler. -//! -//! StableMIR users should not use any of the items in this module directly. -//! These APIs have no stability guarantee. - -use std::cell::Cell; - -use crate::abi::{FnAbi, Layout, LayoutShape}; -use crate::crate_def::Attribute; -use crate::mir::alloc::{AllocId, GlobalAlloc}; -use crate::mir::mono::{Instance, InstanceDef, StaticDef}; -use crate::mir::{BinOp, Body, Place, UnOp}; -use crate::target::MachineInfo; -use crate::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, - ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, - ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl, - TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, -}; -use crate::{ - AssocItems, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, - ItemKind, Symbol, TraitDecls, mir, -}; - -/// This trait defines the interface between stable_mir and the Rust compiler. -/// Do not use this directly. -pub trait Context { - fn entry_fn(&self) -> Option; - /// Retrieve all items of the local crate that have a MIR associated with them. - fn all_local_items(&self) -> CrateItems; - /// Retrieve the body of a function. - /// This function will panic if the body is not available. - fn mir_body(&self, item: DefId) -> mir::Body; - /// Check whether the body of a function is available. - fn has_body(&self, item: DefId) -> bool; - fn foreign_modules(&self, crate_num: CrateNum) -> Vec; - - /// Retrieve all functions defined in this crate. - fn crate_functions(&self, crate_num: CrateNum) -> Vec; - - /// Retrieve all static items defined in this crate. - fn crate_statics(&self, crate_num: CrateNum) -> Vec; - fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule; - fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec; - fn all_trait_decls(&self) -> TraitDecls; - fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls; - fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl; - fn all_trait_impls(&self) -> ImplTraitDecls; - fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls; - fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait; - fn generics_of(&self, def_id: DefId) -> Generics; - fn predicates_of(&self, def_id: DefId) -> GenericPredicates; - fn explicit_predicates_of(&self, def_id: DefId) -> GenericPredicates; - /// Get information about the local crate. - fn local_crate(&self) -> Crate; - /// Retrieve a list of all external crates. - fn external_crates(&self) -> Vec; - - /// Find a crate with the given name. - fn find_crates(&self, name: &str) -> Vec; - - /// Returns the name of given `DefId` - fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; - - /// Return registered tool attributes with the given attribute name. - /// - /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool - /// attributes will simply return an empty list. - /// - /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. - /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. - fn tool_attrs(&self, def_id: DefId, attr: &[Symbol]) -> Vec; - - /// Get all tool attributes of a definition. - fn all_tool_attrs(&self, def_id: DefId) -> Vec; - - /// Returns printable, human readable form of `Span` - fn span_to_string(&self, span: Span) -> String; - - /// Return filename from given `Span`, for diagnostic purposes - fn get_filename(&self, span: &Span) -> Filename; - - /// Return lines corresponding to this `Span` - fn get_lines(&self, span: &Span) -> LineInfo; - - /// Returns the `kind` of given `DefId` - fn item_kind(&self, item: CrateItem) -> ItemKind; - - /// Returns whether this is a foreign item. - fn is_foreign_item(&self, item: DefId) -> bool; - - /// Returns the kind of a given foreign item. - fn foreign_item_kind(&self, def: ForeignDef) -> ForeignItemKind; - - /// Returns the kind of a given algebraic data type - fn adt_kind(&self, def: AdtDef) -> AdtKind; - - /// Returns if the ADT is a box. - fn adt_is_box(&self, def: AdtDef) -> bool; - - /// Returns whether this ADT is simd. - fn adt_is_simd(&self, def: AdtDef) -> bool; - - /// Returns whether this definition is a C string. - fn adt_is_cstr(&self, def: AdtDef) -> bool; - - /// Retrieve the function signature for the given generic arguments. - fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig; - - /// Retrieve the intrinsic definition if the item corresponds one. - fn intrinsic(&self, item: DefId) -> Option; - - /// Retrieve the plain function name of an intrinsic. - fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol; - - /// Retrieve the closure signature for the given generic arguments. - fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig; - - /// The number of variants in this ADT. - fn adt_variants_len(&self, def: AdtDef) -> usize; - - /// The name of a variant. - fn variant_name(&self, def: VariantDef) -> Symbol; - fn variant_fields(&self, def: VariantDef) -> Vec; - - /// Evaluate constant as a target usize. - fn eval_target_usize(&self, cnst: &MirConst) -> Result; - fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result; - - /// Create a new zero-sized constant. - fn try_new_const_zst(&self, ty: Ty) -> Result; - - /// Create a new constant that represents the given string value. - fn new_const_str(&self, value: &str) -> MirConst; - - /// Create a new constant that represents the given boolean value. - fn new_const_bool(&self, value: bool) -> MirConst; - - /// Create a new constant that represents the given value. - fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; - fn try_new_ty_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; - - /// Create a new type from the given kind. - fn new_rigid_ty(&self, kind: RigidTy) -> Ty; - - /// Create a new box type, `Box`, for the given inner type `T`. - fn new_box_ty(&self, ty: Ty) -> Ty; - - /// Returns the type of given crate item. - fn def_ty(&self, item: DefId) -> Ty; - - /// Returns the type of given definition instantiated with the given arguments. - fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty; - - /// Returns literal value of a const as a string. - fn mir_const_pretty(&self, cnst: &MirConst) -> String; - - /// `Span` of an item - fn span_of_an_item(&self, def_id: DefId) -> Span; - - fn ty_const_pretty(&self, ct: TyConstId) -> String; - - /// Obtain the representation of a type. - fn ty_pretty(&self, ty: Ty) -> String; - - /// Obtain the representation of a type. - fn ty_kind(&self, ty: Ty) -> TyKind; - - // Get the discriminant Ty for this Ty if there's one. - fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> Ty; - - /// Get the body of an Instance which is already monomorphized. - fn instance_body(&self, instance: InstanceDef) -> Option; - - /// Get the instance type with generic instantiations applied and lifetimes erased. - fn instance_ty(&self, instance: InstanceDef) -> Ty; - - /// Get the instantiation types. - fn instance_args(&self, def: InstanceDef) -> GenericArgs; - - /// Get the instance. - fn instance_def_id(&self, instance: InstanceDef) -> DefId; - - /// Get the instance mangled name. - fn instance_mangled_name(&self, instance: InstanceDef) -> Symbol; - - /// Check if this is an empty DropGlue shim. - fn is_empty_drop_shim(&self, def: InstanceDef) -> bool; - - /// Check if this is an empty AsyncDropGlueCtor shim. - fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool; - - /// Convert a non-generic crate item into an instance. - /// This function will panic if the item is generic. - fn mono_instance(&self, def_id: DefId) -> Instance; - - /// Item requires monomorphization. - fn requires_monomorphization(&self, def_id: DefId) -> bool; - - /// Resolve an instance from the given function definition and generic arguments. - fn resolve_instance(&self, def: FnDef, args: &GenericArgs) -> Option; - - /// Resolve an instance for drop_in_place for the given type. - fn resolve_drop_in_place(&self, ty: Ty) -> Instance; - - /// Resolve instance for a function pointer. - fn resolve_for_fn_ptr(&self, def: FnDef, args: &GenericArgs) -> Option; - - /// Resolve instance for a closure with the requested type. - fn resolve_closure( - &self, - def: ClosureDef, - args: &GenericArgs, - kind: ClosureKind, - ) -> Option; - - /// Evaluate a static's initializer. - fn eval_static_initializer(&self, def: StaticDef) -> Result; - - /// Try to evaluate an instance into a constant. - fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result; - - /// Retrieve global allocation for the given allocation ID. - fn global_alloc(&self, id: AllocId) -> GlobalAlloc; - - /// Retrieve the id for the virtual table. - fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option; - fn krate(&self, def_id: DefId) -> Crate; - fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol; - - /// Return information about the target machine. - fn target_info(&self) -> MachineInfo; - - /// Get an instance ABI. - fn instance_abi(&self, def: InstanceDef) -> Result; - - /// Get the ABI of a function pointer. - fn fn_ptr_abi(&self, fn_ptr: PolyFnSig) -> Result; - - /// Get the layout of a type. - fn ty_layout(&self, ty: Ty) -> Result; - - /// Get the layout shape. - fn layout_shape(&self, id: Layout) -> LayoutShape; - - /// Get a debug string representation of a place. - fn place_pretty(&self, place: &Place) -> String; - - /// Get the resulting type of binary operation. - fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; - - /// Get the resulting type of unary operation. - fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty; - - /// Get all associated items of a definition. - fn associated_items(&self, def_id: DefId) -> AssocItems; -} - -// A thread local variable that stores a pointer to the tables mapping between TyCtxt -// datastructures and stable MIR datastructures -scoped_tls::scoped_thread_local!(static TLV: Cell<*const ()>); - -pub fn run(context: &dyn Context, f: F) -> Result -where - F: FnOnce() -> T, -{ - if TLV.is_set() { - Err(Error::from("StableMIR already running")) - } else { - let ptr: *const () = (&raw const context) as _; - TLV.set(&Cell::new(ptr), || Ok(f())) - } -} - -/// Execute the given function with access the compiler [Context]. -/// -/// I.e., This function will load the current context and calls a function with it. -/// Do not nest these, as that will ICE. -pub(crate) fn with(f: impl FnOnce(&dyn Context) -> R) -> R { - assert!(TLV.is_set()); - TLV.with(|tlv| { - let ptr = tlv.get(); - assert!(!ptr.is_null()); - f(unsafe { *(ptr as *const &dyn Context) }) - }) -} diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs deleted file mode 100644 index 75228135e4c..00000000000 --- a/compiler/stable_mir/src/crate_def.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! Module that define a common trait for things that represent a crate definition, -//! such as, a function, a trait, an enum, and any other definitions. - -use serde::Serialize; - -use crate::ty::{GenericArgs, Span, Ty}; -use crate::{AssocItems, Crate, Symbol, with}; - -/// A unique identification number for each item accessible for the current compilation unit. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize)] -pub struct DefId(pub(crate) usize); - -impl DefId { - /// Return fully qualified name of this definition - pub fn name(&self) -> Symbol { - with(|cx| cx.def_name(*self, false)) - } - - /// Return a trimmed name of this definition. - /// - /// This can be used to print more user friendly diagnostic messages. - /// - /// If a symbol name can only be imported from one place for a type, and as - /// long as it was not glob-imported anywhere in the current crate, we trim its - /// path and print only the name. - /// - /// For example, this function may shorten `std::vec::Vec` to just `Vec`, - /// as long as there is no other `Vec` importable anywhere. - pub fn trimmed_name(&self) -> Symbol { - with(|cx| cx.def_name(*self, true)) - } -} - -/// A trait for retrieving information about a particular definition. -/// -/// Implementors must provide the implementation of `def_id` which will be used to retrieve -/// information about a crate's definition. -pub trait CrateDef { - /// Retrieve the unique identifier for the current definition. - fn def_id(&self) -> DefId; - - /// Return the fully qualified name of the current definition. - /// - /// See [`DefId::name`] for more details - fn name(&self) -> Symbol { - self.def_id().name() - } - - /// Return a trimmed name of this definition. - /// - /// See [`DefId::trimmed_name`] for more details - fn trimmed_name(&self) -> Symbol { - self.def_id().trimmed_name() - } - - /// Return information about the crate where this definition is declared. - /// - /// This will return the crate number and its name. - fn krate(&self) -> Crate { - let def_id = self.def_id(); - with(|cx| cx.krate(def_id)) - } - - /// Return the span of this definition. - fn span(&self) -> Span { - let def_id = self.def_id(); - with(|cx| cx.span_of_an_item(def_id)) - } - - /// Return registered tool attributes with the given attribute name. - /// - /// FIXME(jdonszelmann): may panic on non-tool attributes. After more attribute work, non-tool - /// attributes will simply return an empty list. - /// - /// Single segmented name like `#[clippy]` is specified as `&["clippy".to_string()]`. - /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. - fn tool_attrs(&self, attr: &[Symbol]) -> Vec { - let def_id = self.def_id(); - with(|cx| cx.tool_attrs(def_id, attr)) - } - - /// Return all tool attributes of this definition. - fn all_tool_attrs(&self) -> Vec { - let def_id = self.def_id(); - with(|cx| cx.all_tool_attrs(def_id)) - } -} - -/// A trait that can be used to retrieve a definition's type. -/// -/// Note that not every CrateDef has a type `Ty`. They should not implement this trait. -pub trait CrateDefType: CrateDef { - /// Returns the type of this crate item. - fn ty(&self) -> Ty { - with(|cx| cx.def_ty(self.def_id())) - } - - /// Retrieve the type of this definition by instantiating and normalizing it with `args`. - /// - /// This will panic if instantiation fails. - fn ty_with_args(&self, args: &GenericArgs) -> Ty { - with(|cx| cx.def_ty_with_args(self.def_id(), args)) - } -} - -/// A trait for retrieving all items from a definition within a crate. -pub trait CrateDefItems: CrateDef { - /// Retrieve all associated items from a definition. - fn associated_items(&self) -> AssocItems { - with(|cx| cx.associated_items(self.def_id())) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Attribute { - value: String, - span: Span, -} - -impl Attribute { - pub fn new(value: String, span: Span) -> Attribute { - Attribute { value, span } - } - - /// Get the span of this attribute. - pub fn span(&self) -> Span { - self.span - } - - /// Get the string representation of this attribute. - pub fn as_str(&self) -> &str { - &self.value - } -} - -macro_rules! crate_def { - ( $(#[$attr:meta])* - $vis:vis $name:ident $(;)? - ) => { - $(#[$attr])* - #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] - $vis struct $name(pub DefId); - - impl CrateDef for $name { - fn def_id(&self) -> DefId { - self.0 - } - } - }; -} - -macro_rules! crate_def_with_ty { - ( $(#[$attr:meta])* - $vis:vis $name:ident $(;)? - ) => { - $(#[$attr])* - #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] - $vis struct $name(pub DefId); - - impl CrateDef for $name { - fn def_id(&self) -> DefId { - self.0 - } - } - - impl CrateDefType for $name {} - }; -} - -macro_rules! impl_crate_def_items { - ( $name:ident $(;)? ) => { - impl CrateDefItems for $name {} - }; -} diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs deleted file mode 100644 index 050752e41eb..00000000000 --- a/compiler/stable_mir/src/error.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! When things go wrong, we need some error handling. -//! There are a few different types of errors in StableMIR: -//! -//! - [CompilerError]: This represents errors that can be raised when invoking the compiler. -//! - [Error]: Generic error that represents the reason why a request that could not be fulfilled. - -use std::fmt::{Debug, Display, Formatter}; -use std::{fmt, io}; - -macro_rules! error { - ($fmt: literal $(,)?) => { Error(format!($fmt)) }; - ($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) }; -} - -pub(crate) use error; - -/// An error type used to represent an error that has already been reported by the compiler. -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum CompilerError { - /// Compilation failed, either due to normal errors or ICE. - Failed, - /// Compilation was interrupted. - Interrupted(T), - /// Compilation skipped. This happens when users invoke rustc to retrieve information such as - /// --version. - Skipped, -} - -/// A generic error to represent an API request that cannot be fulfilled. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Error(pub(crate) String); - -impl Error { - pub fn new(msg: String) -> Self { - Self(msg) - } -} - -impl From<&str> for Error { - fn from(value: &str) -> Self { - Self(value.into()) - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) - } -} - -impl Display for CompilerError -where - T: Display, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - CompilerError::Failed => write!(f, "Compilation Failed"), - CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason}"), - CompilerError::Skipped => write!(f, "Compilation Skipped"), - } - } -} - -impl Debug for CompilerError -where - T: Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - CompilerError::Failed => write!(f, "Compilation Failed"), - CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason:?}"), - CompilerError::Skipped => write!(f, "Compilation Skipped"), - } - } -} - -impl std::error::Error for Error {} - -impl std::error::Error for CompilerError where T: Display + Debug {} - -impl From for Error { - fn from(value: io::Error) -> Self { - Error(value.to_string()) - } -} diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index df90d3e5a08..cc0fb52433d 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -1,231 +1,7 @@ -//! The WIP stable interface to rustc internals. +//! We've temporarily moved the `stable_mir` implementation to [`rustc_smir::stable_mir`], +//! during refactoring to break the circular dependency between `rustc_smir` and `stable_mir`, //! -//! For more information see -//! -//! # Note -//! -//! This API is still completely unstable and subject to change. - -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", - test(attr(allow(unused_variables), deny(warnings))) -)] -//! -//! This crate shall contain all type definitions and APIs that we expect third-party tools to invoke to -//! interact with the compiler. -//! -//! The goal is to eventually be published on -//! [crates.io](https://crates.io). - -use std::fmt::Debug; -use std::{fmt, io}; - -use serde::Serialize; - -use crate::compiler_interface::with; -pub use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType, DefId}; -pub use crate::error::*; -use crate::mir::mono::StaticDef; -use crate::mir::{Body, Mutability}; -use crate::ty::{AssocItem, FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty}; - -pub mod abi; -#[macro_use] -pub mod crate_def; -pub mod compiler_interface; -#[macro_use] -pub mod error; -pub mod mir; -pub mod target; -pub mod ty; -pub mod visitor; - -/// Use String for now but we should replace it. -pub type Symbol = String; - -/// The number that identifies a crate. -pub type CrateNum = usize; - -impl Debug for DefId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DefId").field("id", &self.0).field("name", &self.name()).finish() - } -} - -impl IndexedVal for DefId { - fn to_val(index: usize) -> Self { - DefId(index) - } - - fn to_index(&self) -> usize { - self.0 - } -} - -/// A list of crate items. -pub type CrateItems = Vec; - -/// A list of trait decls. -pub type TraitDecls = Vec; - -/// A list of impl trait decls. -pub type ImplTraitDecls = Vec; - -/// A list of associated items. -pub type AssocItems = Vec; - -/// Holds information about a crate. -#[derive(Clone, PartialEq, Eq, Debug, Serialize)] -pub struct Crate { - pub id: CrateNum, - pub name: Symbol, - pub is_local: bool, -} - -impl Crate { - /// The list of foreign modules in this crate. - pub fn foreign_modules(&self) -> Vec { - with(|cx| cx.foreign_modules(self.id)) - } - - /// The list of traits declared in this crate. - pub fn trait_decls(&self) -> TraitDecls { - with(|cx| cx.trait_decls(self.id)) - } - - /// The list of trait implementations in this crate. - pub fn trait_impls(&self) -> ImplTraitDecls { - with(|cx| cx.trait_impls(self.id)) - } - - /// Return a list of function definitions from this crate independent on their visibility. - pub fn fn_defs(&self) -> Vec { - with(|cx| cx.crate_functions(self.id)) - } - - /// Return a list of static items defined in this crate independent on their visibility. - pub fn statics(&self) -> Vec { - with(|cx| cx.crate_statics(self.id)) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] -pub enum ItemKind { - Fn, - Static, - Const, - Ctor(CtorKind), -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)] -pub enum CtorKind { - Const, - Fn, -} - -pub type Filename = String; - -crate_def_with_ty! { - /// Holds information about an item in a crate. - #[derive(Serialize)] - pub CrateItem; -} - -impl CrateItem { - /// This will return the body of an item or panic if it's not available. - pub fn expect_body(&self) -> mir::Body { - with(|cx| cx.mir_body(self.0)) - } - - /// Return the body of an item if available. - pub fn body(&self) -> Option { - with(|cx| cx.has_body(self.0).then(|| cx.mir_body(self.0))) - } - - /// Check if a body is available for this item. - pub fn has_body(&self) -> bool { - with(|cx| cx.has_body(self.0)) - } - - pub fn span(&self) -> Span { - with(|cx| cx.span_of_an_item(self.0)) - } - - pub fn kind(&self) -> ItemKind { - with(|cx| cx.item_kind(*self)) - } - - pub fn requires_monomorphization(&self) -> bool { - with(|cx| cx.requires_monomorphization(self.0)) - } - - pub fn ty(&self) -> Ty { - with(|cx| cx.def_ty(self.0)) - } - - pub fn is_foreign_item(&self) -> bool { - with(|cx| cx.is_foreign_item(self.0)) - } - - /// Emit MIR for this item body. - pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { - self.body() - .ok_or_else(|| io::Error::other(format!("No body found for `{}`", self.name())))? - .dump(w, &self.name()) - } -} - -/// Return the function where execution starts if the current -/// crate defines that. This is usually `main`, but could be -/// `start` if the crate is a no-std crate. -pub fn entry_fn() -> Option { - with(|cx| cx.entry_fn()) -} - -/// Access to the local crate. -pub fn local_crate() -> Crate { - with(|cx| cx.local_crate()) -} - -/// Try to find a crate or crates if multiple crates exist from given name. -pub fn find_crates(name: &str) -> Vec { - with(|cx| cx.find_crates(name)) -} - -/// Try to find a crate with the given name. -pub fn external_crates() -> Vec { - with(|cx| cx.external_crates()) -} - -/// Retrieve all items in the local crate that have a MIR associated with them. -pub fn all_local_items() -> CrateItems { - with(|cx| cx.all_local_items()) -} - -pub fn all_trait_decls() -> TraitDecls { - with(|cx| cx.all_trait_decls()) -} - -pub fn all_trait_impls() -> ImplTraitDecls { - with(|cx| cx.all_trait_impls()) -} - -/// A type that provides internal information but that can still be used for debug purpose. -#[derive(Clone, PartialEq, Eq, Hash, Serialize)] -pub struct Opaque(String); - -impl std::fmt::Display for Opaque { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::Debug for Opaque { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} +//! This is a transitional measure as described in [PR #139319](https://github.com/rust-lang/rust/pull/139319). +//! Once the refactoring is complete, the `stable_mir` implementation will be moved back here. -pub fn opaque(value: &T) -> Opaque { - Opaque(format!("{value:?}")) -} +pub use rustc_smir::stable_mir::*; diff --git a/compiler/stable_mir/src/mir.rs b/compiler/stable_mir/src/mir.rs deleted file mode 100644 index 413b5152bb3..00000000000 --- a/compiler/stable_mir/src/mir.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod alloc; -mod body; -pub mod mono; -pub mod pretty; -pub mod visit; - -pub use body::*; -pub use visit::{MirVisitor, MutMirVisitor}; diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs deleted file mode 100644 index 023807b76ae..00000000000 --- a/compiler/stable_mir/src/mir/alloc.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! This module provides methods to retrieve allocation information, such as static variables. - -use std::io::Read; - -use serde::Serialize; - -use crate::mir::mono::{Instance, StaticDef}; -use crate::target::{Endian, MachineInfo}; -use crate::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty}; -use crate::{Error, with}; - -/// An allocation in the SMIR global memory can be either a function pointer, -/// a static, or a "real" allocation with some data in it. -#[derive(Debug, Clone, Eq, PartialEq, Serialize)] -pub enum GlobalAlloc { - /// The alloc ID is used as a function pointer. - Function(Instance), - /// This alloc ID points to a symbolic (not-reified) vtable. - /// The `None` trait ref is used to represent auto traits. - VTable(Ty, Option>), - /// The alloc ID points to a "lazy" static variable that did not get computed (yet). - /// This is also used to break the cycle in recursive statics. - Static(StaticDef), - /// The alloc ID points to memory. - Memory(Allocation), -} - -impl From for GlobalAlloc { - fn from(value: AllocId) -> Self { - with(|cx| cx.global_alloc(value)) - } -} - -impl GlobalAlloc { - /// Retrieve the allocation id for a global allocation if it exists. - /// - /// For `[GlobalAlloc::VTable]`, this will return the allocation for the VTable of the given - /// type for the optional trait if the type implements the trait. - /// - /// This method will always return `None` for allocations other than `[GlobalAlloc::VTable]`. - pub fn vtable_allocation(&self) -> Option { - with(|cx| cx.vtable_allocation(self)) - } -} - -/// A unique identification number for each provenance -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] -pub struct AllocId(usize); - -impl IndexedVal for AllocId { - fn to_val(index: usize) -> Self { - AllocId(index) - } - fn to_index(&self) -> usize { - self.0 - } -} - -/// Utility function used to read an allocation data into a unassigned integer. -pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result { - let mut buf = [0u8; size_of::()]; - match MachineInfo::target_endianness() { - Endian::Little => { - bytes.read_exact(&mut buf[..bytes.len()])?; - Ok(u128::from_le_bytes(buf)) - } - Endian::Big => { - bytes.read_exact(&mut buf[16 - bytes.len()..])?; - Ok(u128::from_be_bytes(buf)) - } - } -} - -/// Utility function used to read an allocation data into an assigned integer. -pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result { - let mut buf = [0u8; size_of::()]; - match MachineInfo::target_endianness() { - Endian::Little => { - bytes.read_exact(&mut buf[..bytes.len()])?; - Ok(i128::from_le_bytes(buf)) - } - Endian::Big => { - bytes.read_exact(&mut buf[16 - bytes.len()..])?; - Ok(i128::from_be_bytes(buf)) - } - } -} diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs deleted file mode 100644 index 2a1c163de3c..00000000000 --- a/compiler/stable_mir/src/mir/body.rs +++ /dev/null @@ -1,1108 +0,0 @@ -use std::io; - -use serde::Serialize; - -use crate::compiler_interface::with; -use crate::mir::pretty::function_body; -use crate::ty::{ - AdtDef, ClosureDef, CoroutineClosureDef, CoroutineDef, GenericArgs, MirConst, Movability, - Region, RigidTy, Ty, TyConst, TyKind, VariantIdx, -}; -use crate::{Error, Opaque, Span, Symbol}; - -/// The SMIR representation of a single function. -#[derive(Clone, Debug, Serialize)] -pub struct Body { - pub blocks: Vec, - - /// Declarations of locals within the function. - /// - /// The first local is the return value pointer, followed by `arg_count` - /// locals for the function arguments, followed by any user-declared - /// variables and temporaries. - pub(super) locals: LocalDecls, - - /// The number of arguments this function takes. - pub(super) arg_count: usize, - - /// Debug information pertaining to user variables, including captures. - pub var_debug_info: Vec, - - /// Mark an argument (which must be a tuple) as getting passed as its individual components. - /// - /// This is used for the "rust-call" ABI such as closures. - pub(super) spread_arg: Option, - - /// The span that covers the entire function body. - pub span: Span, -} - -pub type BasicBlockIdx = usize; - -impl Body { - /// Constructs a `Body`. - /// - /// A constructor is required to build a `Body` from outside the crate - /// because the `arg_count` and `locals` fields are private. - pub fn new( - blocks: Vec, - locals: LocalDecls, - arg_count: usize, - var_debug_info: Vec, - spread_arg: Option, - span: Span, - ) -> Self { - // If locals doesn't contain enough entries, it can lead to panics in - // `ret_local`, `arg_locals`, and `inner_locals`. - assert!( - locals.len() > arg_count, - "A Body must contain at least a local for the return value and each of the function's arguments" - ); - Self { blocks, locals, arg_count, var_debug_info, spread_arg, span } - } - - /// Return local that holds this function's return value. - pub fn ret_local(&self) -> &LocalDecl { - &self.locals[RETURN_LOCAL] - } - - /// Locals in `self` that correspond to this function's arguments. - pub fn arg_locals(&self) -> &[LocalDecl] { - &self.locals[1..][..self.arg_count] - } - - /// Inner locals for this function. These are the locals that are - /// neither the return local nor the argument locals. - pub fn inner_locals(&self) -> &[LocalDecl] { - &self.locals[self.arg_count + 1..] - } - - /// Returns a mutable reference to the local that holds this function's return value. - pub(crate) fn ret_local_mut(&mut self) -> &mut LocalDecl { - &mut self.locals[RETURN_LOCAL] - } - - /// Returns a mutable slice of locals corresponding to this function's arguments. - pub(crate) fn arg_locals_mut(&mut self) -> &mut [LocalDecl] { - &mut self.locals[1..][..self.arg_count] - } - - /// Returns a mutable slice of inner locals for this function. - /// Inner locals are those that are neither the return local nor the argument locals. - pub(crate) fn inner_locals_mut(&mut self) -> &mut [LocalDecl] { - &mut self.locals[self.arg_count + 1..] - } - - /// Convenience function to get all the locals in this function. - /// - /// Locals are typically accessed via the more specific methods `ret_local`, - /// `arg_locals`, and `inner_locals`. - pub fn locals(&self) -> &[LocalDecl] { - &self.locals - } - - /// Get the local declaration for this local. - pub fn local_decl(&self, local: Local) -> Option<&LocalDecl> { - self.locals.get(local) - } - - /// Get an iterator for all local declarations. - pub fn local_decls(&self) -> impl Iterator { - self.locals.iter().enumerate() - } - - /// Emit the body using the provided name for the signature. - pub fn dump(&self, w: &mut W, fn_name: &str) -> io::Result<()> { - function_body(w, self, fn_name) - } - - pub fn spread_arg(&self) -> Option { - self.spread_arg - } -} - -type LocalDecls = Vec; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct LocalDecl { - pub ty: Ty, - pub span: Span, - pub mutability: Mutability, -} - -#[derive(Clone, PartialEq, Eq, Debug, Serialize)] -pub struct BasicBlock { - pub statements: Vec, - pub terminator: Terminator, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Terminator { - pub kind: TerminatorKind, - pub span: Span, -} - -impl Terminator { - pub fn successors(&self) -> Successors { - self.kind.successors() - } -} - -pub type Successors = Vec; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum TerminatorKind { - Goto { - target: BasicBlockIdx, - }, - SwitchInt { - discr: Operand, - targets: SwitchTargets, - }, - Resume, - Abort, - Return, - Unreachable, - Drop { - place: Place, - target: BasicBlockIdx, - unwind: UnwindAction, - }, - Call { - func: Operand, - args: Vec, - destination: Place, - target: Option, - unwind: UnwindAction, - }, - Assert { - cond: Operand, - expected: bool, - msg: AssertMessage, - target: BasicBlockIdx, - unwind: UnwindAction, - }, - InlineAsm { - template: String, - operands: Vec, - options: String, - line_spans: String, - destination: Option, - unwind: UnwindAction, - }, -} - -impl TerminatorKind { - pub fn successors(&self) -> Successors { - use self::TerminatorKind::*; - match *self { - Call { target: Some(t), unwind: UnwindAction::Cleanup(u), .. } - | Drop { target: t, unwind: UnwindAction::Cleanup(u), .. } - | Assert { target: t, unwind: UnwindAction::Cleanup(u), .. } - | InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(u), .. } => { - vec![t, u] - } - Goto { target: t } - | Call { target: None, unwind: UnwindAction::Cleanup(t), .. } - | Call { target: Some(t), unwind: _, .. } - | Drop { target: t, unwind: _, .. } - | Assert { target: t, unwind: _, .. } - | InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. } - | InlineAsm { destination: Some(t), unwind: _, .. } => { - vec![t] - } - - Return - | Resume - | Abort - | Unreachable - | Call { target: None, unwind: _, .. } - | InlineAsm { destination: None, unwind: _, .. } => { - vec![] - } - SwitchInt { ref targets, .. } => targets.all_targets(), - } - } - - pub fn unwind(&self) -> Option<&UnwindAction> { - match *self { - TerminatorKind::Goto { .. } - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::SwitchInt { .. } => None, - TerminatorKind::Call { ref unwind, .. } - | TerminatorKind::Assert { ref unwind, .. } - | TerminatorKind::Drop { ref unwind, .. } - | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct InlineAsmOperand { - pub in_value: Option, - pub out_place: Option, - // This field has a raw debug representation of MIR's InlineAsmOperand. - // For now we care about place/operand + the rest in a debug format. - pub raw_rpr: String, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum UnwindAction { - Continue, - Unreachable, - Terminate, - Cleanup(BasicBlockIdx), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AssertMessage { - BoundsCheck { len: Operand, index: Operand }, - Overflow(BinOp, Operand, Operand), - OverflowNeg(Operand), - DivisionByZero(Operand), - RemainderByZero(Operand), - ResumedAfterReturn(CoroutineKind), - ResumedAfterPanic(CoroutineKind), - MisalignedPointerDereference { required: Operand, found: Operand }, - NullPointerDereference, -} - -impl AssertMessage { - pub fn description(&self) -> Result<&'static str, Error> { - match self { - AssertMessage::Overflow(BinOp::Add, _, _) => Ok("attempt to add with overflow"), - AssertMessage::Overflow(BinOp::Sub, _, _) => Ok("attempt to subtract with overflow"), - AssertMessage::Overflow(BinOp::Mul, _, _) => Ok("attempt to multiply with overflow"), - AssertMessage::Overflow(BinOp::Div, _, _) => Ok("attempt to divide with overflow"), - AssertMessage::Overflow(BinOp::Rem, _, _) => { - Ok("attempt to calculate the remainder with overflow") - } - AssertMessage::OverflowNeg(_) => Ok("attempt to negate with overflow"), - AssertMessage::Overflow(BinOp::Shr, _, _) => Ok("attempt to shift right with overflow"), - AssertMessage::Overflow(BinOp::Shl, _, _) => Ok("attempt to shift left with overflow"), - AssertMessage::Overflow(op, _, _) => Err(error!("`{:?}` cannot overflow", op)), - AssertMessage::DivisionByZero(_) => Ok("attempt to divide by zero"), - AssertMessage::RemainderByZero(_) => { - Ok("attempt to calculate the remainder with a divisor of zero") - } - AssertMessage::ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { - Ok("coroutine resumed after completion") - } - AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( - CoroutineDesugaring::Async, - _, - )) => Ok("`async fn` resumed after completion"), - AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( - CoroutineDesugaring::Gen, - _, - )) => Ok("`async gen fn` resumed after completion"), - AssertMessage::ResumedAfterReturn(CoroutineKind::Desugared( - CoroutineDesugaring::AsyncGen, - _, - )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after completion"), - AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { - Ok("coroutine resumed after panicking") - } - AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( - CoroutineDesugaring::Async, - _, - )) => Ok("`async fn` resumed after panicking"), - AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( - CoroutineDesugaring::Gen, - _, - )) => Ok("`async gen fn` resumed after panicking"), - AssertMessage::ResumedAfterPanic(CoroutineKind::Desugared( - CoroutineDesugaring::AsyncGen, - _, - )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"), - - AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"), - AssertMessage::MisalignedPointerDereference { .. } => { - Ok("misaligned pointer dereference") - } - AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"), - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum BinOp { - Add, - AddUnchecked, - Sub, - SubUnchecked, - Mul, - MulUnchecked, - Div, - Rem, - BitXor, - BitAnd, - BitOr, - Shl, - ShlUnchecked, - Shr, - ShrUnchecked, - Eq, - Lt, - Le, - Ne, - Ge, - Gt, - Cmp, - Offset, -} - -impl BinOp { - /// Return the type of this operation for the given input Ty. - /// This function does not perform type checking, and it currently doesn't handle SIMD. - pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty { - with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty)) - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum UnOp { - Not, - Neg, - PtrMetadata, -} - -impl UnOp { - /// Return the type of this operation for the given input Ty. - /// This function does not perform type checking, and it currently doesn't handle SIMD. - pub fn ty(&self, arg_ty: Ty) -> Ty { - with(|ctx| ctx.unop_ty(*self, arg_ty)) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum CoroutineKind { - Desugared(CoroutineDesugaring, CoroutineSource), - Coroutine(Movability), -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum CoroutineSource { - Block, - Closure, - Fn, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum CoroutineDesugaring { - Async, - - Gen, - - AsyncGen, -} - -pub(crate) type LocalDefId = Opaque; -/// The rustc coverage data structures are heavily tied to internal details of the -/// coverage implementation that are likely to change, and are unlikely to be -/// useful to third-party tools for the foreseeable future. -pub(crate) type Coverage = Opaque; - -/// The FakeReadCause describes the type of pattern why a FakeRead statement exists. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum FakeReadCause { - ForMatchGuard, - ForMatchedPlace(LocalDefId), - ForGuardBinding, - ForLet(LocalDefId), - ForIndex, -} - -/// Describes what kind of retag is to be performed -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] -pub enum RetagKind { - FnEntry, - TwoPhase, - Raw, - Default, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] -pub enum Variance { - Covariant, - Invariant, - Contravariant, - Bivariant, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct CopyNonOverlapping { - pub src: Operand, - pub dst: Operand, - pub count: Operand, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum NonDivergingIntrinsic { - Assume(Operand), - CopyNonOverlapping(CopyNonOverlapping), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Statement { - pub kind: StatementKind, - pub span: Span, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum StatementKind { - Assign(Place, Rvalue), - FakeRead(FakeReadCause, Place), - SetDiscriminant { place: Place, variant_index: VariantIdx }, - Deinit(Place), - StorageLive(Local), - StorageDead(Local), - Retag(RetagKind, Place), - PlaceMention(Place), - AscribeUserType { place: Place, projections: UserTypeProjection, variance: Variance }, - Coverage(Coverage), - Intrinsic(NonDivergingIntrinsic), - ConstEvalCounter, - Nop, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Rvalue { - /// Creates a pointer with the indicated mutability to the place. - /// - /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like - /// `&raw v` or `addr_of!(v)`. - AddressOf(RawPtrKind, Place), - - /// Creates an aggregate value, like a tuple or struct. - /// - /// This is needed because dataflow analysis needs to distinguish - /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo` - /// has a destructor. - /// - /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After - /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too. - Aggregate(AggregateKind, Vec), - - /// * `Offset` has the same semantics as `<*const T>::offset`, except that the second - /// parameter may be a `usize` as well. - /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats, - /// raw pointers, or function pointers and return a `bool`. The types of the operands must be - /// matching, up to the usual caveat of the lifetimes in function pointers. - /// * Left and right shift operations accept signed or unsigned integers not necessarily of the - /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is - /// truncated as needed. - /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching - /// types and return a value of that type. - /// * The remaining operations accept signed integers, unsigned integers, or floats with - /// matching types and return a value of that type. - BinaryOp(BinOp, Operand, Operand), - - /// Performs essentially all of the casts that can be performed via `as`. - /// - /// This allows for casts from/to a variety of types. - Cast(CastKind, Operand, Ty), - - /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. - /// - /// For addition, subtraction, and multiplication on integers the error condition is set when - /// the infinite precision result would not be equal to the actual result. - CheckedBinaryOp(BinOp, Operand, Operand), - - /// A CopyForDeref is equivalent to a read from a place. - /// When such a read happens, it is guaranteed that the only use of the returned value is a - /// deref operation, immediately followed by one or more projections. - CopyForDeref(Place), - - /// Computes the discriminant of the place, returning it as an integer. - /// Returns zero for types without discriminant. - /// - /// The validity requirements for the underlying value are undecided for this rvalue, see - /// [#91095]. Note too that the value of the discriminant is not the same thing as the - /// variant index; - /// - /// [#91095]: https://github.com/rust-lang/rust/issues/91095 - Discriminant(Place), - - /// Yields the length of the place, as a `usize`. - /// - /// If the type of the place is an array, this is the array length. For slices (`[T]`, not - /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is - /// ill-formed for places of other types. - Len(Place), - - /// Creates a reference to the place. - Ref(Region, BorrowKind, Place), - - /// Creates an array where each element is the value of the operand. - /// - /// This is the cause of a bug in the case where the repetition count is zero because the value - /// is not dropped, see [#74836]. - /// - /// Corresponds to source code like `[x; 32]`. - /// - /// [#74836]: https://github.com/rust-lang/rust/issues/74836 - Repeat(Operand, TyConst), - - /// Transmutes a `*mut u8` into shallow-initialized `Box`. - /// - /// This is different from a normal transmute because dataflow analysis will treat the box as - /// initialized but its content as uninitialized. Like other pointer casts, this in general - /// affects alias analysis. - ShallowInitBox(Operand, Ty), - - /// Creates a pointer/reference to the given thread local. - /// - /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a - /// `*const T`, and if neither of those apply a `&T`. - /// - /// **Note:** This is a runtime operation that actually executes code and is in this sense more - /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to - /// SIGILL for some reason that I (JakobDegen) never got a chance to look into. - /// - /// **Needs clarification**: Are there weird additional semantics here related to the runtime - /// nature of this operation? - ThreadLocalRef(crate::CrateItem), - - /// Computes a value as described by the operation. - NullaryOp(NullOp, Ty), - - /// Exactly like `BinaryOp`, but less operands. - /// - /// Also does two's-complement arithmetic. Negation requires a signed integer or a float; - /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds - /// return a value with the same type as their operand. - UnaryOp(UnOp, Operand), - - /// Yields the operand unchanged - Use(Operand), -} - -impl Rvalue { - pub fn ty(&self, locals: &[LocalDecl]) -> Result { - match self { - Rvalue::Use(operand) => operand.ty(locals), - Rvalue::Repeat(operand, count) => { - Ok(Ty::new_array_with_const_len(operand.ty(locals)?, count.clone())) - } - Rvalue::ThreadLocalRef(did) => Ok(did.ty()), - Rvalue::Ref(reg, bk, place) => { - let place_ty = place.ty(locals)?; - Ok(Ty::new_ref(reg.clone(), place_ty, bk.to_mutable_lossy())) - } - Rvalue::AddressOf(mutability, place) => { - let place_ty = place.ty(locals)?; - Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) - } - Rvalue::Len(..) => Ok(Ty::usize_ty()), - Rvalue::Cast(.., ty) => Ok(*ty), - Rvalue::BinaryOp(op, lhs, rhs) => { - let lhs_ty = lhs.ty(locals)?; - let rhs_ty = rhs.ty(locals)?; - Ok(op.ty(lhs_ty, rhs_ty)) - } - Rvalue::CheckedBinaryOp(op, lhs, rhs) => { - let lhs_ty = lhs.ty(locals)?; - let rhs_ty = rhs.ty(locals)?; - let ty = op.ty(lhs_ty, rhs_ty); - Ok(Ty::new_tuple(&[ty, Ty::bool_ty()])) - } - Rvalue::UnaryOp(op, operand) => { - let arg_ty = operand.ty(locals)?; - Ok(op.ty(arg_ty)) - } - Rvalue::Discriminant(place) => { - let place_ty = place.ty(locals)?; - place_ty - .kind() - .discriminant_ty() - .ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}")) - } - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { - Ok(Ty::usize_ty()) - } - Rvalue::NullaryOp(NullOp::ContractChecks, _) - | Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), - Rvalue::Aggregate(ak, ops) => match *ak { - AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), - AggregateKind::Tuple => Ok(Ty::new_tuple( - &ops.iter().map(|op| op.ty(locals)).collect::, _>>()?, - )), - AggregateKind::Adt(def, _, ref args, _, _) => Ok(def.ty_with_args(args)), - AggregateKind::Closure(def, ref args) => Ok(Ty::new_closure(def, args.clone())), - AggregateKind::Coroutine(def, ref args, mov) => { - Ok(Ty::new_coroutine(def, args.clone(), mov)) - } - AggregateKind::CoroutineClosure(def, ref args) => { - Ok(Ty::new_coroutine_closure(def, args.clone())) - } - AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), - }, - Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), - Rvalue::CopyForDeref(place) => place.ty(locals), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AggregateKind { - Array(Ty), - Tuple, - Adt(AdtDef, VariantIdx, GenericArgs, Option, Option), - Closure(ClosureDef, GenericArgs), - // FIXME(stable_mir): Movability here is redundant - Coroutine(CoroutineDef, GenericArgs, Movability), - CoroutineClosure(CoroutineClosureDef, GenericArgs), - RawPtr(Ty, Mutability), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Operand { - Copy(Place), - Move(Place), - Constant(ConstOperand), -} - -#[derive(Clone, Eq, PartialEq, Serialize)] -pub struct Place { - pub local: Local, - /// projection out of a place (access a field, deref a pointer, etc) - pub projection: Vec, -} - -impl From for Place { - fn from(local: Local) -> Self { - Place { local, projection: vec![] } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ConstOperand { - pub span: Span, - pub user_ty: Option, - pub const_: MirConst, -} - -/// Debug information pertaining to a user variable. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct VarDebugInfo { - /// The variable name. - pub name: Symbol, - - /// Source info of the user variable, including the scope - /// within which the variable is visible (to debuginfo). - pub source_info: SourceInfo, - - /// The user variable's data is split across several fragments, - /// each described by a `VarDebugInfoFragment`. - pub composite: Option, - - /// Where the data for this user variable is to be found. - pub value: VarDebugInfoContents, - - /// When present, indicates what argument number this variable is in the function that it - /// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the - /// argument number in the original function before it was inlined. - pub argument_index: Option, -} - -impl VarDebugInfo { - /// Return a local variable if this info is related to one. - pub fn local(&self) -> Option { - match &self.value { - VarDebugInfoContents::Place(place) if place.projection.is_empty() => Some(place.local), - VarDebugInfoContents::Place(_) | VarDebugInfoContents::Const(_) => None, - } - } - - /// Return a constant if this info is related to one. - pub fn constant(&self) -> Option<&ConstOperand> { - match &self.value { - VarDebugInfoContents::Place(_) => None, - VarDebugInfoContents::Const(const_op) => Some(const_op), - } - } -} - -pub type SourceScope = u32; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct SourceInfo { - pub span: Span, - pub scope: SourceScope, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct VarDebugInfoFragment { - pub ty: Ty, - pub projection: Vec, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum VarDebugInfoContents { - Place(Place), - Const(ConstOperand), -} - -// In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This -// is so it can be used for both Places (for which the projection elements are of type -// ProjectionElem) and user-provided type annotations (for which the projection elements -// are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use -// ProjectionElem for Places. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum ProjectionElem { - /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place. - Deref, - - /// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is - /// referenced by source-order index rather than the name of the field. The fields type is also - /// given. - Field(FieldIdx, Ty), - - /// Index into a slice/array. The value of the index is computed at runtime using the `V` - /// argument. - /// - /// Note that this does not also dereference, and so it does not exactly correspond to slice - /// indexing in Rust. In other words, in the below Rust code: - /// - /// ```rust - /// let x = &[1, 2, 3, 4]; - /// let i = 2; - /// x[i]; - /// ``` - /// - /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same - /// thing is true of the `ConstantIndex` and `Subslice` projections below. - Index(Local), - - /// Index into a slice/array given by offsets. - /// - /// These indices are generated by slice patterns. Easiest to explain by example: - /// - /// ```ignore (illustrative) - /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, - /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, - /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, - /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, - /// ``` - ConstantIndex { - /// index or -index (in Python terms), depending on from_end - offset: u64, - /// The thing being indexed must be at least this long -- otherwise, the - /// projection is UB. - /// - /// For arrays this is always the exact length. - min_length: u64, - /// Counting backwards from end? This is always false when indexing an - /// array. - from_end: bool, - }, - - /// Projects a slice from the base place. - /// - /// These indices are generated by slice patterns. If `from_end` is true, this represents - /// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`. - Subslice { - from: u64, - to: u64, - /// Whether `to` counts from the start or end of the array/slice. - from_end: bool, - }, - - /// "Downcast" to a variant of an enum or a coroutine. - Downcast(VariantIdx), - - /// Like an explicit cast from an opaque type to a concrete type, but without - /// requiring an intermediate variable. - OpaqueCast(Ty), - - /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where - /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping - /// explicit during optimizations and codegen. - /// - /// This projection doesn't impact the runtime behavior of the program except for potentially changing - /// some type metadata of the interpreter or codegen backend. - Subtype(Ty), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct UserTypeProjection { - pub base: UserTypeAnnotationIndex, - - pub projection: Opaque, -} - -pub type Local = usize; - -pub const RETURN_LOCAL: Local = 0; - -/// The source-order index of a field in a variant. -/// -/// For example, in the following types, -/// ```ignore(illustrative) -/// enum Demo1 { -/// Variant0 { a: bool, b: i32 }, -/// Variant1 { c: u8, d: u64 }, -/// } -/// struct Demo2 { e: u8, f: u16, g: u8 } -/// ``` -/// `a`'s `FieldIdx` is `0`, -/// `b`'s `FieldIdx` is `1`, -/// `c`'s `FieldIdx` is `0`, and -/// `g`'s `FieldIdx` is `2`. -pub type FieldIdx = usize; - -type UserTypeAnnotationIndex = usize; - -/// The possible branch sites of a [TerminatorKind::SwitchInt]. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct SwitchTargets { - /// The conditional branches where the first element represents the value that guards this - /// branch, and the second element is the branch target. - branches: Vec<(u128, BasicBlockIdx)>, - /// The `otherwise` branch which will be taken in case none of the conditional branches are - /// satisfied. - otherwise: BasicBlockIdx, -} - -impl SwitchTargets { - /// All possible targets including the `otherwise` target. - pub fn all_targets(&self) -> Successors { - self.branches.iter().map(|(_, target)| *target).chain(Some(self.otherwise)).collect() - } - - /// The `otherwise` branch target. - pub fn otherwise(&self) -> BasicBlockIdx { - self.otherwise - } - - /// The conditional targets which are only taken if the pattern matches the given value. - pub fn branches(&self) -> impl Iterator { - self.branches.iter().copied() - } - - /// The number of targets including `otherwise`. - pub fn len(&self) -> usize { - self.branches.len() + 1 - } - - /// Create a new SwitchTargets from the given branches and `otherwise` target. - pub fn new(branches: Vec<(u128, BasicBlockIdx)>, otherwise: BasicBlockIdx) -> SwitchTargets { - SwitchTargets { branches, otherwise } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum BorrowKind { - /// Data must be immutable and is aliasable. - Shared, - - /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either - /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]). - Fake(FakeBorrowKind), - - /// Data is mutable and not aliasable. - Mut { - /// `true` if this borrow arose from method-call auto-ref - kind: MutBorrowKind, - }, -} - -impl BorrowKind { - pub fn to_mutable_lossy(self) -> Mutability { - match self { - BorrowKind::Mut { .. } => Mutability::Mut, - BorrowKind::Shared => Mutability::Not, - // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. - BorrowKind::Fake(_) => Mutability::Not, - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum RawPtrKind { - Mut, - Const, - FakeForPtrMetadata, -} - -impl RawPtrKind { - pub fn to_mutable_lossy(self) -> Mutability { - match self { - RawPtrKind::Mut { .. } => Mutability::Mut, - RawPtrKind::Const => Mutability::Not, - // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. - RawPtrKind::FakeForPtrMetadata => Mutability::Not, - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum MutBorrowKind { - Default, - TwoPhaseBorrow, - ClosureCapture, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum FakeBorrowKind { - /// A shared (deep) borrow. Data must be immutable and is aliasable. - Deep, - /// The immediately borrowed place must be immutable, but projections from - /// it don't need to be. This is used to prevent match guards from replacing - /// the scrutinee. For example, a fake borrow of `a.b` doesn't - /// conflict with a mutable borrow of `a.b.c`. - Shallow, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum Mutability { - Not, - Mut, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Safety { - Safe, - Unsafe, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum PointerCoercion { - /// Go from a fn-item type to a fn-pointer type. - ReifyFnPointer, - - /// Go from a safe fn pointer to an unsafe fn pointer. - UnsafeFnPointer, - - /// Go from a non-capturing closure to a fn pointer or an unsafe fn pointer. - /// It cannot convert a closure that requires unsafe. - ClosureFnPointer(Safety), - - /// Go from a mut raw pointer to a const raw pointer. - MutToConstPointer, - - /// Go from `*const [T; N]` to `*const T` - ArrayToPointer, - - /// Unsize a pointer/reference value, e.g., `&[T; n]` to - /// `&[T]`. Note that the source could be a thin or wide pointer. - /// This will do things like convert thin pointers to wide - /// pointers, or convert structs containing thin pointers to - /// structs containing wide pointers, or convert between wide - /// pointers. - Unsize, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum CastKind { - // FIXME(smir-rename): rename this to PointerExposeProvenance - PointerExposeAddress, - PointerWithExposedProvenance, - PointerCoercion(PointerCoercion), - // FIXME(smir-rename): change this to PointerCoercion(DynStar) - DynStar, - IntToInt, - FloatToInt, - FloatToFloat, - IntToFloat, - PtrToPtr, - FnPtrToPtr, - Transmute, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum NullOp { - /// Returns the size of a value of that type. - SizeOf, - /// Returns the minimum alignment of a type. - AlignOf, - /// Returns the offset of a field. - OffsetOf(Vec<(VariantIdx, FieldIdx)>), - /// cfg!(ub_checks), but at codegen time - UbChecks, - /// cfg!(contract_checks), but at codegen time - ContractChecks, -} - -impl Operand { - /// Get the type of an operand relative to the local declaration. - /// - /// In order to retrieve the correct type, the `locals` argument must match the list of all - /// locals from the function body where this operand originates from. - /// - /// Errors indicate a malformed operand or incompatible locals list. - pub fn ty(&self, locals: &[LocalDecl]) -> Result { - match self { - Operand::Copy(place) | Operand::Move(place) => place.ty(locals), - Operand::Constant(c) => Ok(c.ty()), - } - } -} - -impl ConstOperand { - pub fn ty(&self) -> Ty { - self.const_.ty() - } -} - -impl Place { - /// Resolve down the chain of projections to get the type referenced at the end of it. - /// E.g.: - /// Calling `ty()` on `var.field` should return the type of `field`. - /// - /// In order to retrieve the correct type, the `locals` argument must match the list of all - /// locals from the function body where this place originates from. - pub fn ty(&self, locals: &[LocalDecl]) -> Result { - self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty)) - } -} - -impl ProjectionElem { - /// Get the expected type after applying this projection to a given place type. - pub fn ty(&self, place_ty: Ty) -> Result { - let ty = place_ty; - match &self { - ProjectionElem::Deref => Self::deref_ty(ty), - ProjectionElem::Field(_idx, fty) => Ok(*fty), - ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => Self::index_ty(ty), - ProjectionElem::Subslice { from, to, from_end } => { - Self::subslice_ty(ty, *from, *to, *from_end) - } - ProjectionElem::Downcast(_) => Ok(ty), - ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), - } - } - - fn index_ty(ty: Ty) -> Result { - ty.kind().builtin_index().ok_or_else(|| error!("Cannot index non-array type: {ty:?}")) - } - - fn subslice_ty(ty: Ty, from: u64, to: u64, from_end: bool) -> Result { - let ty_kind = ty.kind(); - match ty_kind { - TyKind::RigidTy(RigidTy::Slice(..)) => Ok(ty), - TyKind::RigidTy(RigidTy::Array(inner, _)) if !from_end => Ty::try_new_array( - inner, - to.checked_sub(from).ok_or_else(|| error!("Subslice overflow: {from}..{to}"))?, - ), - TyKind::RigidTy(RigidTy::Array(inner, size)) => { - let size = size.eval_target_usize()?; - let len = size - from - to; - Ty::try_new_array(inner, len) - } - _ => Err(Error(format!("Cannot subslice non-array type: `{ty_kind:?}`"))), - } - } - - fn deref_ty(ty: Ty) -> Result { - let deref_ty = ty - .kind() - .builtin_deref(true) - .ok_or_else(|| error!("Cannot dereference type: {ty:?}"))?; - Ok(deref_ty.ty) - } -} diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs deleted file mode 100644 index 22507a49411..00000000000 --- a/compiler/stable_mir/src/mir/mono.rs +++ /dev/null @@ -1,307 +0,0 @@ -use std::fmt::{Debug, Formatter}; -use std::io; - -use serde::Serialize; - -use crate::abi::FnAbi; -use crate::crate_def::CrateDef; -use crate::mir::Body; -use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; -use crate::{CrateItem, DefId, Error, ItemKind, Opaque, Symbol, with}; - -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum MonoItem { - Fn(Instance), - Static(StaticDef), - GlobalAsm(Opaque), -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize)] -pub struct Instance { - /// The type of instance. - pub kind: InstanceKind, - /// An ID used to get the instance definition from the compiler. - /// Do not use this field directly. - pub def: InstanceDef, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)] -pub enum InstanceKind { - /// A user defined item. - Item, - /// A compiler intrinsic function. - Intrinsic, - /// A virtual function definition stored in a VTable. - /// The `idx` field indicates the position in the VTable for this instance. - Virtual { idx: usize }, - /// A compiler generated shim. - Shim, -} - -impl Instance { - /// Get the arguments this instance was instantiated with. - pub fn args(&self) -> GenericArgs { - with(|cx| cx.instance_args(self.def)) - } - - /// Get the body of an Instance. - /// - /// The body will be eagerly monomorphized and all constants will already be evaluated. - /// - /// This method will return the intrinsic fallback body if one was defined. - pub fn body(&self) -> Option { - with(|context| context.instance_body(self.def)) - } - - /// Check whether this instance has a body available. - /// - /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide - /// whether to specialize the intrinsic or to use its fallback body. - /// - /// For more information on fallback body, see . - /// - /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build - /// the StableMIR body. - pub fn has_body(&self) -> bool { - with(|cx| cx.has_body(self.def.def_id())) - } - - pub fn is_foreign_item(&self) -> bool { - with(|cx| cx.is_foreign_item(self.def.def_id())) - } - - /// Get the instance type with generic instantiations applied and lifetimes erased. - pub fn ty(&self) -> Ty { - with(|context| context.instance_ty(self.def)) - } - - /// Retrieve information about this instance binary interface. - pub fn fn_abi(&self) -> Result { - with(|cx| cx.instance_abi(self.def)) - } - - /// Retrieve the instance's mangled name used for calling the given instance. - /// - /// This will also look up the correct name of instances from upstream crates. - pub fn mangled_name(&self) -> Symbol { - with(|context| context.instance_mangled_name(self.def)) - } - - /// Retrieve the instance name for diagnostic messages. - /// - /// This will return the specialized name, e.g., `std::vec::Vec::new`. - pub fn name(&self) -> Symbol { - with(|context| context.instance_name(self.def, false)) - } - - /// Return a trimmed name of the given instance including its args. - /// - /// If a symbol name can only be imported from one place for a type, and as - /// long as it was not glob-imported anywhere in the current crate, we trim its - /// path and print only the name. - pub fn trimmed_name(&self) -> Symbol { - with(|context| context.instance_name(self.def, true)) - } - - /// Retrieve the plain intrinsic name of an instance if it's an intrinsic. - /// - /// The plain name does not include type arguments (as `trimmed_name` does), - /// which is more convenient to match with intrinsic symbols. - pub fn intrinsic_name(&self) -> Option { - match self.kind { - InstanceKind::Intrinsic => { - Some(with(|context| context.intrinsic(self.def.def_id()).unwrap().fn_name())) - } - InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None, - } - } - - /// Resolve an instance starting from a function definition and generic arguments. - pub fn resolve(def: FnDef, args: &GenericArgs) -> Result { - with(|context| { - context.resolve_instance(def, args).ok_or_else(|| { - crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) - }) - }) - } - - /// Resolve the drop in place for a given type. - pub fn resolve_drop_in_place(ty: Ty) -> Instance { - with(|cx| cx.resolve_drop_in_place(ty)) - } - - /// Resolve an instance for a given function pointer. - pub fn resolve_for_fn_ptr(def: FnDef, args: &GenericArgs) -> Result { - with(|context| { - context.resolve_for_fn_ptr(def, args).ok_or_else(|| { - crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) - }) - }) - } - - /// Resolve a closure with the expected kind. - pub fn resolve_closure( - def: ClosureDef, - args: &GenericArgs, - kind: ClosureKind, - ) -> Result { - with(|context| { - context.resolve_closure(def, args, kind).ok_or_else(|| { - crate::Error::new(format!("Failed to resolve `{def:?}` with `{args:?}`")) - }) - }) - } - - /// Check whether this instance is an empty shim. - /// - /// Allow users to check if this shim can be ignored when called directly. - /// - /// We have decided not to export different types of Shims to StableMIR users, however, this - /// is a query that can be very helpful for users when processing DropGlue. - /// - /// When generating code for a Drop terminator, users can ignore an empty drop glue. - /// These shims are only needed to generate a valid Drop call done via VTable. - pub fn is_empty_shim(&self) -> bool { - self.kind == InstanceKind::Shim - && with(|cx| { - cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def) - }) - } - - /// Try to constant evaluate the instance into a constant with the given type. - /// - /// This can be used to retrieve a constant that represents an intrinsic return such as - /// `type_id`. - pub fn try_const_eval(&self, const_ty: Ty) -> Result { - with(|cx| cx.eval_instance(self.def, const_ty)) - } - - /// Emit the body of this instance if it has one. - pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { - if let Some(body) = self.body() { body.dump(w, &self.name()) } else { Ok(()) } - } -} - -impl Debug for Instance { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Instance") - .field("kind", &self.kind) - .field("def", &self.mangled_name()) - .field("args", &self.args()) - .finish() - } -} - -/// Try to convert a crate item into an instance. -/// The item cannot be generic in order to be converted into an instance. -impl TryFrom for Instance { - type Error = crate::Error; - - fn try_from(item: CrateItem) -> Result { - with(|context| { - let def_id = item.def_id(); - if !context.requires_monomorphization(def_id) { - Ok(context.mono_instance(def_id)) - } else { - Err(Error::new("Item requires monomorphization".to_string())) - } - }) - } -} - -/// Try to convert an instance into a crate item. -/// Only user defined instances can be converted. -impl TryFrom for CrateItem { - type Error = crate::Error; - - fn try_from(value: Instance) -> Result { - with(|context| { - if value.kind == InstanceKind::Item && context.has_body(value.def.def_id()) { - Ok(CrateItem(context.instance_def_id(value.def))) - } else { - Err(Error::new(format!("Item kind `{:?}` cannot be converted", value.kind))) - } - }) - } -} - -impl From for MonoItem { - fn from(value: Instance) -> Self { - MonoItem::Fn(value) - } -} - -impl From for MonoItem { - fn from(value: StaticDef) -> Self { - MonoItem::Static(value) - } -} - -impl From for CrateItem { - fn from(value: StaticDef) -> Self { - CrateItem(value.0) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct InstanceDef(usize); - -impl CrateDef for InstanceDef { - fn def_id(&self) -> DefId { - with(|context| context.instance_def_id(*self)) - } -} - -crate_def! { - /// Holds information about a static variable definition. - #[derive(Serialize)] - pub StaticDef; -} - -impl TryFrom for StaticDef { - type Error = crate::Error; - - fn try_from(value: CrateItem) -> Result { - if matches!(value.kind(), ItemKind::Static) { - Ok(StaticDef(value.0)) - } else { - Err(Error::new(format!("Expected a static item, but found: {value:?}"))) - } - } -} - -impl TryFrom for StaticDef { - type Error = crate::Error; - - fn try_from(value: Instance) -> Result { - StaticDef::try_from(CrateItem::try_from(value)?) - } -} - -impl From for Instance { - fn from(value: StaticDef) -> Self { - // A static definition should always be convertible to an instance. - with(|cx| cx.mono_instance(value.def_id())) - } -} - -impl StaticDef { - /// Return the type of this static definition. - pub fn ty(&self) -> Ty { - with(|cx| cx.def_ty(self.0)) - } - - /// Evaluate a static's initializer, returning the allocation of the initializer's memory. - pub fn eval_initializer(&self) -> Result { - with(|cx| cx.eval_static_initializer(*self)) - } -} - -impl IndexedVal for InstanceDef { - fn to_val(index: usize) -> Self { - InstanceDef(index) - } - fn to_index(&self) -> usize { - self.0 - } -} diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs deleted file mode 100644 index 65d9f20f0a3..00000000000 --- a/compiler/stable_mir/src/mir/pretty.rs +++ /dev/null @@ -1,462 +0,0 @@ -//! Implement methods to pretty print stable MIR body. -use std::fmt::Debug; -use std::io::Write; -use std::{fmt, io, iter}; - -use fmt::{Display, Formatter}; - -use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind}; -use crate::mir::{ - Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents, -}; -use crate::ty::{AdtKind, AssocKind, IndexedVal, MirConst, Ty, TyConst}; -use crate::{Body, CrateDef, Mutability, with}; - -impl Display for Ty { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - with(|ctx| write!(f, "{}", ctx.ty_pretty(*self))) - } -} - -impl Display for AssocKind { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - AssocKind::Fn => write!(f, "method"), - AssocKind::Const => write!(f, "associated const"), - AssocKind::Type => write!(f, "associated type"), - } - } -} - -impl Debug for Place { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - with(|ctx| write!(f, "{}", ctx.place_pretty(self))) - } -} - -pub(crate) fn function_body(writer: &mut W, body: &Body, name: &str) -> io::Result<()> { - write!(writer, "fn {name}(")?; - let mut sep = ""; - for (index, local) in body.arg_locals().iter().enumerate() { - write!(writer, "{}_{}: {}", sep, index + 1, local.ty)?; - sep = ", "; - } - write!(writer, ")")?; - - let return_local = body.ret_local(); - writeln!(writer, " -> {} {{", return_local.ty)?; - - body.locals().iter().enumerate().try_for_each(|(index, local)| -> io::Result<()> { - if index == 0 || index > body.arg_count { - writeln!(writer, " let {}_{}: {};", pretty_mut(local.mutability), index, local.ty) - } else { - Ok(()) - } - })?; - - body.var_debug_info.iter().try_for_each(|info| { - let content = match &info.value { - VarDebugInfoContents::Place(place) => { - format!("{place:?}") - } - VarDebugInfoContents::Const(constant) => pretty_mir_const(&constant.const_), - }; - writeln!(writer, " debug {} => {};", info.name, content) - })?; - - body.blocks - .iter() - .enumerate() - .map(|(index, block)| -> io::Result<()> { - writeln!(writer, " bb{index}: {{")?; - let _ = block - .statements - .iter() - .map(|statement| -> io::Result<()> { - pretty_statement(writer, &statement.kind)?; - Ok(()) - }) - .collect::>(); - pretty_terminator(writer, &block.terminator.kind)?; - writeln!(writer, " }}").unwrap(); - Ok(()) - }) - .collect::, _>>()?; - writeln!(writer, "}}")?; - Ok(()) -} - -fn pretty_statement(writer: &mut W, statement: &StatementKind) -> io::Result<()> { - const INDENT: &str = " "; - match statement { - StatementKind::Assign(place, rval) => { - write!(writer, "{INDENT}{place:?} = ")?; - pretty_rvalue(writer, rval)?; - writeln!(writer, ";") - } - // FIXME: Add rest of the statements - StatementKind::FakeRead(cause, place) => { - writeln!(writer, "{INDENT}FakeRead({cause:?}, {place:?});") - } - StatementKind::SetDiscriminant { place, variant_index } => { - writeln!(writer, "{INDENT}discriminant({place:?} = {};", variant_index.to_index()) - } - StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"), - StatementKind::StorageLive(local) => { - writeln!(writer, "{INDENT}StorageLive(_{local});") - } - StatementKind::StorageDead(local) => { - writeln!(writer, "{INDENT}StorageDead(_{local});") - } - StatementKind::Retag(kind, place) => writeln!(writer, "Retag({kind:?}, {place:?});"), - StatementKind::PlaceMention(place) => { - writeln!(writer, "{INDENT}PlaceMention({place:?};") - } - StatementKind::ConstEvalCounter => { - writeln!(writer, "{INDENT}ConstEvalCounter;") - } - StatementKind::Nop => writeln!(writer, "{INDENT}nop;"), - StatementKind::AscribeUserType { .. } - | StatementKind::Coverage(_) - | StatementKind::Intrinsic(_) => { - // FIX-ME: Make them pretty. - writeln!(writer, "{INDENT}{statement:?};") - } - } -} - -fn pretty_terminator(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { - pretty_terminator_head(writer, terminator)?; - let successors = terminator.successors(); - let successor_count = successors.len(); - let labels = pretty_successor_labels(terminator); - - let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_))); - let fmt_unwind = |w: &mut W| -> io::Result<()> { - write!(w, "unwind ")?; - match terminator.unwind() { - None | Some(UnwindAction::Cleanup(_)) => unreachable!(), - Some(UnwindAction::Continue) => write!(w, "continue"), - Some(UnwindAction::Unreachable) => write!(w, "unreachable"), - Some(UnwindAction::Terminate) => write!(w, "terminate"), - } - }; - - match (successor_count, show_unwind) { - (0, false) => {} - (0, true) => { - write!(writer, " -> ")?; - fmt_unwind(writer)?; - } - (1, false) => write!(writer, " -> bb{:?}", successors[0])?, - _ => { - write!(writer, " -> [")?; - for (i, target) in successors.iter().enumerate() { - if i > 0 { - write!(writer, ", ")?; - } - write!(writer, "{}: bb{:?}", labels[i], target)?; - } - if show_unwind { - write!(writer, ", ")?; - fmt_unwind(writer)?; - } - write!(writer, "]")?; - } - }; - - writeln!(writer, ";") -} - -fn pretty_terminator_head(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { - use self::TerminatorKind::*; - const INDENT: &str = " "; - match terminator { - Goto { .. } => write!(writer, "{INDENT}goto"), - SwitchInt { discr, .. } => { - write!(writer, "{INDENT}switchInt({})", pretty_operand(discr)) - } - Resume => write!(writer, "{INDENT}resume"), - Abort => write!(writer, "{INDENT}abort"), - Return => write!(writer, "{INDENT}return"), - Unreachable => write!(writer, "{INDENT}unreachable"), - Drop { place, .. } => write!(writer, "{INDENT}drop({place:?})"), - Call { func, args, destination, .. } => { - write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?; - let mut args_iter = args.iter(); - args_iter.next().map_or(Ok(()), |arg| write!(writer, "{}", pretty_operand(arg)))?; - args_iter.try_for_each(|arg| write!(writer, ", {}", pretty_operand(arg)))?; - write!(writer, ")") - } - Assert { cond, expected, msg, target: _, unwind: _ } => { - write!(writer, "{INDENT}assert(")?; - if !expected { - write!(writer, "!")?; - } - write!(writer, "{}, ", pretty_operand(cond))?; - pretty_assert_message(writer, msg)?; - write!(writer, ")") - } - InlineAsm { .. } => write!(writer, "{INDENT}InlineAsm"), - } -} - -fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec { - use self::TerminatorKind::*; - match terminator { - Call { target: None, unwind: UnwindAction::Cleanup(_), .. } - | InlineAsm { destination: None, .. } => vec!["unwind".into()], - Resume | Abort | Return | Unreachable | Call { target: None, unwind: _, .. } => vec![], - Goto { .. } => vec!["".to_string()], - SwitchInt { targets, .. } => targets - .branches() - .map(|(val, _target)| format!("{val}")) - .chain(iter::once("otherwise".into())) - .collect(), - Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()], - Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => { - vec!["return".into(), "unwind".into()] - } - Drop { unwind: _, .. } | Call { target: Some(_), unwind: _, .. } => vec!["return".into()], - Assert { unwind: UnwindAction::Cleanup(_), .. } => { - vec!["success".into(), "unwind".into()] - } - Assert { unwind: _, .. } => vec!["success".into()], - InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()], - } -} - -fn pretty_assert_message(writer: &mut W, msg: &AssertMessage) -> io::Result<()> { - match msg { - AssertMessage::BoundsCheck { len, index } => { - let pretty_len = pretty_operand(len); - let pretty_index = pretty_operand(index); - write!( - writer, - "\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}" - ) - } - AssertMessage::Overflow(BinOp::Add, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Sub, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Mul, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Div, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Rem, l, r) => { - let pretty_l = pretty_operand(l); - let pretty_r = pretty_operand(r); - write!( - writer, - "\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}" - ) - } - AssertMessage::Overflow(BinOp::Shr, _, r) => { - let pretty_r = pretty_operand(r); - write!(writer, "\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}") - } - AssertMessage::Overflow(BinOp::Shl, _, r) => { - let pretty_r = pretty_operand(r); - write!(writer, "\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}") - } - AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op), - AssertMessage::OverflowNeg(op) => { - let pretty_op = pretty_operand(op); - write!(writer, "\"attempt to negate `{{}}`, which would overflow\", {pretty_op}") - } - AssertMessage::DivisionByZero(op) => { - let pretty_op = pretty_operand(op); - write!(writer, "\"attempt to divide `{{}}` by zero\", {pretty_op}") - } - AssertMessage::RemainderByZero(op) => { - let pretty_op = pretty_operand(op); - write!( - writer, - "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}" - ) - } - AssertMessage::MisalignedPointerDereference { required, found } => { - let pretty_required = pretty_operand(required); - let pretty_found = pretty_operand(found); - write!( - writer, - "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}" - ) - } - AssertMessage::NullPointerDereference => { - write!(writer, "\"null pointer dereference occurred\"") - } - AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { - write!(writer, "{}", msg.description().unwrap()) - } - } -} - -fn pretty_operand(operand: &Operand) -> String { - match operand { - Operand::Copy(copy) => { - format!("{copy:?}") - } - Operand::Move(mv) => { - format!("move {mv:?}") - } - Operand::Constant(cnst) => pretty_mir_const(&cnst.const_), - } -} - -fn pretty_mir_const(literal: &MirConst) -> String { - with(|cx| cx.mir_const_pretty(literal)) -} - -fn pretty_ty_const(ct: &TyConst) -> String { - with(|cx| cx.ty_const_pretty(ct.id)) -} - -fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { - match rval { - Rvalue::AddressOf(mutability, place) => { - write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place) - } - Rvalue::Aggregate(aggregate_kind, operands) => { - // FIXME: Add pretty_aggregate function that returns a pretty string - pretty_aggregate(writer, aggregate_kind, operands) - } - Rvalue::BinaryOp(bin, op1, op2) => { - write!(writer, "{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) - } - Rvalue::Cast(_, op, ty) => { - write!(writer, "{} as {}", pretty_operand(op), ty) - } - Rvalue::CheckedBinaryOp(bin, op1, op2) => { - write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) - } - Rvalue::CopyForDeref(deref) => { - write!(writer, "CopyForDeref({deref:?})") - } - Rvalue::Discriminant(place) => { - write!(writer, "discriminant({place:?})") - } - Rvalue::Len(len) => { - write!(writer, "len({len:?})") - } - Rvalue::Ref(_, borrowkind, place) => { - let kind = match borrowkind { - BorrowKind::Shared => "&", - BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ", - BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ", - BorrowKind::Mut { .. } => "&mut ", - }; - write!(writer, "{kind}{place:?}") - } - Rvalue::Repeat(op, cnst) => { - write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst)) - } - Rvalue::ShallowInitBox(_, _) => Ok(()), - Rvalue::ThreadLocalRef(item) => { - write!(writer, "thread_local_ref{item:?}") - } - Rvalue::NullaryOp(nul, ty) => { - write!(writer, "{nul:?}::<{ty}>() \" \"") - } - Rvalue::UnaryOp(un, op) => { - write!(writer, "{:?}({})", un, pretty_operand(op)) - } - Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)), - } -} - -fn pretty_aggregate( - writer: &mut W, - aggregate_kind: &AggregateKind, - operands: &Vec, -) -> io::Result<()> { - let suffix = match aggregate_kind { - AggregateKind::Array(_) => { - write!(writer, "[")?; - "]" - } - AggregateKind::Tuple => { - write!(writer, "(")?; - ")" - } - AggregateKind::Adt(def, var, _, _, _) => { - if def.kind() == AdtKind::Enum { - write!(writer, "{}::{}", def.name(), def.variant(*var).unwrap().name())?; - } else { - write!(writer, "{}", def.variant(*var).unwrap().name())?; - } - if operands.is_empty() { - return Ok(()); - } - // FIXME: Change this once we have CtorKind in StableMIR. - write!(writer, "(")?; - ")" - } - AggregateKind::Closure(def, _) => { - write!(writer, "{{closure@{}}}(", def.span().diagnostic())?; - ")" - } - AggregateKind::Coroutine(def, _, _) => { - write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?; - ")" - } - AggregateKind::CoroutineClosure(def, _) => { - write!(writer, "{{coroutine-closure@{}}}(", def.span().diagnostic())?; - ")" - } - AggregateKind::RawPtr(ty, mutability) => { - write!( - writer, - "*{} {ty} from (", - if *mutability == Mutability::Mut { "mut" } else { "const" } - )?; - ")" - } - }; - let mut separator = ""; - for op in operands { - write!(writer, "{}{}", separator, pretty_operand(op))?; - separator = ", "; - } - write!(writer, "{suffix}") -} - -fn pretty_mut(mutability: Mutability) -> &'static str { - match mutability { - Mutability::Not => " ", - Mutability::Mut => "mut ", - } -} - -fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str { - match kind { - RawPtrKind::Const => "const", - RawPtrKind::Mut => "mut", - RawPtrKind::FakeForPtrMetadata => "const (fake)", - } -} diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs deleted file mode 100644 index 9d2368ba332..00000000000 --- a/compiler/stable_mir/src/mir/visit.rs +++ /dev/null @@ -1,586 +0,0 @@ -//! # The Stable MIR Visitor -//! -//! ## Overview -//! -//! We currently only support an immutable visitor. -//! The structure of this visitor is similar to the ones internal to `rustc`, -//! and it follows the following conventions: -//! -//! For every mir item, the trait has a `visit_` and a `super_` method. -//! - `visit_`, by default, calls `super_` -//! - `super_`, by default, destructures the `` and calls `visit_` for -//! all sub-items that compose the original item. -//! -//! In order to implement a visitor, override the `visit_*` methods for the types you are -//! interested in analyzing, and invoke (within that method call) -//! `self.super_*` to continue to the traverse. -//! Avoid calling `super` methods in other circumstances. -//! -//! For the most part, we do not destructure things external to the -//! MIR, e.g., types, spans, etc, but simply visit them and stop. -//! This avoids duplication with other visitors like `TypeFoldable`. -//! -//! ## Updating -//! -//! The code is written in a very deliberate style intended to minimize -//! the chance of things being overlooked. -//! -//! Use pattern matching to reference fields and ensure that all -//! matches are exhaustive. -//! -//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. -//! That means you never write `..` to skip over fields, nor do you write `_` -//! to skip over variants in a `match`. -//! -//! The only place that `_` is acceptable is to match a field (or -//! variant argument) that does not require visiting. - -use crate::mir::*; -use crate::ty::{GenericArgs, MirConst, Region, Ty, TyConst}; -use crate::{Error, Opaque, Span}; - -macro_rules! make_mir_visitor { - ($visitor_trait_name:ident, $($mutability:ident)?) => { - pub trait $visitor_trait_name { - fn visit_body(&mut self, body: &$($mutability)? Body) { - self.super_body(body) - } - - fn visit_basic_block(&mut self, bb: &$($mutability)? BasicBlock) { - self.super_basic_block(bb) - } - - fn visit_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { - self.super_ret_decl(local, decl) - } - - fn visit_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { - self.super_arg_decl(local, decl) - } - - fn visit_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { - self.super_local_decl(local, decl) - } - - fn visit_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) { - self.super_statement(stmt, location) - } - - fn visit_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) { - self.super_terminator(term, location) - } - - fn visit_span(&mut self, span: &$($mutability)? Span) { - self.super_span(span) - } - - fn visit_place(&mut self, place: &$($mutability)? Place, ptx: PlaceContext, location: Location) { - self.super_place(place, ptx, location) - } - - visit_place_fns!($($mutability)?); - - fn visit_local(&mut self, local: &$($mutability)? Local, ptx: PlaceContext, location: Location) { - let _ = (local, ptx, location); - } - - fn visit_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) { - self.super_rvalue(rvalue, location) - } - - fn visit_operand(&mut self, operand: &$($mutability)? Operand, location: Location) { - self.super_operand(operand, location) - } - - fn visit_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) { - self.super_user_type_projection(projection) - } - - fn visit_ty(&mut self, ty: &$($mutability)? Ty, location: Location) { - let _ = location; - self.super_ty(ty) - } - - fn visit_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) { - self.super_const_operand(constant, location) - } - - fn visit_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) { - self.super_mir_const(constant, location) - } - - fn visit_ty_const(&mut self, constant: &$($mutability)? TyConst, location: Location) { - let _ = location; - self.super_ty_const(constant) - } - - fn visit_region(&mut self, region: &$($mutability)? Region, location: Location) { - let _ = location; - self.super_region(region) - } - - fn visit_args(&mut self, args: &$($mutability)? GenericArgs, location: Location) { - let _ = location; - self.super_args(args) - } - - fn visit_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) { - self.super_assert_msg(msg, location) - } - - fn visit_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) { - self.super_var_debug_info(var_debug_info); - } - - fn super_body(&mut self, body: &$($mutability)? Body) { - super_body!(self, body, $($mutability)?); - } - - fn super_basic_block(&mut self, bb: &$($mutability)? BasicBlock) { - let BasicBlock { statements, terminator } = bb; - for stmt in statements { - self.visit_statement(stmt, Location(stmt.span)); - } - self.visit_terminator(terminator, Location(terminator.span)); - } - - fn super_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { - let _ = local; - let LocalDecl { ty, span, .. } = decl; - self.visit_ty(ty, Location(*span)); - } - - fn super_ret_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { - self.super_local_decl(local, decl) - } - - fn super_arg_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { - self.super_local_decl(local, decl) - } - - fn super_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) { - let Statement { kind, span } = stmt; - self.visit_span(span); - match kind { - StatementKind::Assign(place, rvalue) => { - self.visit_place(place, PlaceContext::MUTATING, location); - self.visit_rvalue(rvalue, location); - } - StatementKind::FakeRead(_, place) | StatementKind::PlaceMention(place) => { - self.visit_place(place, PlaceContext::NON_MUTATING, location); - } - StatementKind::SetDiscriminant { place, .. } - | StatementKind::Deinit(place) - | StatementKind::Retag(_, place) => { - self.visit_place(place, PlaceContext::MUTATING, location); - } - StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { - self.visit_local(local, PlaceContext::NON_USE, location); - } - StatementKind::AscribeUserType { place, projections, variance: _ } => { - self.visit_place(place, PlaceContext::NON_USE, location); - self.visit_user_type_projection(projections); - } - StatementKind::Coverage(coverage) => visit_opaque(coverage), - StatementKind::Intrinsic(intrisic) => match intrisic { - NonDivergingIntrinsic::Assume(operand) => { - self.visit_operand(operand, location); - } - NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { - src, - dst, - count, - }) => { - self.visit_operand(src, location); - self.visit_operand(dst, location); - self.visit_operand(count, location); - } - }, - StatementKind::ConstEvalCounter | StatementKind::Nop => {} - } - } - - fn super_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) { - let Terminator { kind, span } = term; - self.visit_span(span); - match kind { - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Unreachable => {} - TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { - self.visit_operand(cond, location); - self.visit_assert_msg(msg, location); - } - TerminatorKind::Drop { place, target: _, unwind: _ } => { - self.visit_place(place, PlaceContext::MUTATING, location); - } - TerminatorKind::Call { func, args, destination, target: _, unwind: _ } => { - self.visit_operand(func, location); - for arg in args { - self.visit_operand(arg, location); - } - self.visit_place(destination, PlaceContext::MUTATING, location); - } - TerminatorKind::InlineAsm { operands, .. } => { - for op in operands { - let InlineAsmOperand { in_value, out_place, raw_rpr: _ } = op; - if let Some(input) = in_value { - self.visit_operand(input, location); - } - if let Some(output) = out_place { - self.visit_place(output, PlaceContext::MUTATING, location); - } - } - } - TerminatorKind::Return => { - let $($mutability)? local = RETURN_LOCAL; - self.visit_local(&$($mutability)? local, PlaceContext::NON_MUTATING, location); - } - TerminatorKind::SwitchInt { discr, targets: _ } => { - self.visit_operand(discr, location); - } - } - } - - fn super_span(&mut self, span: &$($mutability)? Span) { - let _ = span; - } - - fn super_rvalue(&mut self, rvalue: &$($mutability)? Rvalue, location: Location) { - match rvalue { - Rvalue::AddressOf(mutability, place) => { - let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut }; - self.visit_place(place, pcx, location); - } - Rvalue::Aggregate(_, operands) => { - for op in operands { - self.visit_operand(op, location); - } - } - Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { - self.visit_operand(lhs, location); - self.visit_operand(rhs, location); - } - Rvalue::Cast(_, op, ty) => { - self.visit_operand(op, location); - self.visit_ty(ty, location); - } - Rvalue::CopyForDeref(place) | Rvalue::Discriminant(place) | Rvalue::Len(place) => { - self.visit_place(place, PlaceContext::NON_MUTATING, location); - } - Rvalue::Ref(region, kind, place) => { - self.visit_region(region, location); - let pcx = PlaceContext { is_mut: matches!(kind, BorrowKind::Mut { .. }) }; - self.visit_place(place, pcx, location); - } - Rvalue::Repeat(op, constant) => { - self.visit_operand(op, location); - self.visit_ty_const(constant, location); - } - Rvalue::ShallowInitBox(op, ty) => { - self.visit_ty(ty, location); - self.visit_operand(op, location) - } - Rvalue::ThreadLocalRef(_) => {} - Rvalue::NullaryOp(_, ty) => { - self.visit_ty(ty, location); - } - Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => { - self.visit_operand(op, location); - } - } - } - - fn super_operand(&mut self, operand: &$($mutability)? Operand, location: Location) { - match operand { - Operand::Copy(place) | Operand::Move(place) => { - self.visit_place(place, PlaceContext::NON_MUTATING, location) - } - Operand::Constant(constant) => { - self.visit_const_operand(constant, location); - } - } - } - - fn super_user_type_projection(&mut self, projection: &$($mutability)? UserTypeProjection) { - // This is a no-op on mir::Visitor. - let _ = projection; - } - - fn super_ty(&mut self, ty: &$($mutability)? Ty) { - let _ = ty; - } - - fn super_const_operand(&mut self, constant: &$($mutability)? ConstOperand, location: Location) { - let ConstOperand { span, user_ty: _, const_ } = constant; - self.visit_span(span); - self.visit_mir_const(const_, location); - } - - fn super_mir_const(&mut self, constant: &$($mutability)? MirConst, location: Location) { - let MirConst { kind: _, ty, id: _ } = constant; - self.visit_ty(ty, location); - } - - fn super_ty_const(&mut self, constant: &$($mutability)? TyConst) { - let _ = constant; - } - - fn super_region(&mut self, region: &$($mutability)? Region) { - let _ = region; - } - - fn super_args(&mut self, args: &$($mutability)? GenericArgs) { - let _ = args; - } - - fn super_var_debug_info(&mut self, var_debug_info: &$($mutability)? VarDebugInfo) { - let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } = - var_debug_info; - self.visit_span(&$($mutability)? source_info.span); - let location = Location(source_info.span); - if let Some(composite) = composite { - self.visit_ty(&$($mutability)? composite.ty, location); - } - match value { - VarDebugInfoContents::Place(place) => { - self.visit_place(place, PlaceContext::NON_USE, location); - } - VarDebugInfoContents::Const(constant) => { - self.visit_mir_const(&$($mutability)? constant.const_, location); - } - } - } - - fn super_assert_msg(&mut self, msg: &$($mutability)? AssertMessage, location: Location) { - match msg { - AssertMessage::BoundsCheck { len, index } => { - self.visit_operand(len, location); - self.visit_operand(index, location); - } - AssertMessage::Overflow(_, left, right) => { - self.visit_operand(left, location); - self.visit_operand(right, location); - } - AssertMessage::OverflowNeg(op) - | AssertMessage::DivisionByZero(op) - | AssertMessage::RemainderByZero(op) => { - self.visit_operand(op, location); - } - AssertMessage::ResumedAfterReturn(_) - | AssertMessage::ResumedAfterPanic(_) - | AssertMessage::NullPointerDereference => { - //nothing to visit - } - AssertMessage::MisalignedPointerDereference { required, found } => { - self.visit_operand(required, location); - self.visit_operand(found, location); - } - } - } - } - }; -} - -macro_rules! super_body { - ($self:ident, $body:ident, mut) => { - for bb in $body.blocks.iter_mut() { - $self.visit_basic_block(bb); - } - - $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local_mut()); - - for (idx, arg) in $body.arg_locals_mut().iter_mut().enumerate() { - $self.visit_arg_decl(idx + 1, arg) - } - - let local_start = $body.arg_count + 1; - for (idx, arg) in $body.inner_locals_mut().iter_mut().enumerate() { - $self.visit_local_decl(idx + local_start, arg) - } - - for info in $body.var_debug_info.iter_mut() { - $self.visit_var_debug_info(info); - } - - $self.visit_span(&mut $body.span) - }; - - ($self:ident, $body:ident, ) => { - let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = $body; - - for bb in blocks { - $self.visit_basic_block(bb); - } - - $self.visit_ret_decl(RETURN_LOCAL, $body.ret_local()); - - for (idx, arg) in $body.arg_locals().iter().enumerate() { - $self.visit_arg_decl(idx + 1, arg) - } - - let local_start = arg_count + 1; - for (idx, arg) in $body.inner_locals().iter().enumerate() { - $self.visit_local_decl(idx + local_start, arg) - } - - for info in var_debug_info.iter() { - $self.visit_var_debug_info(info); - } - - $self.visit_span(span) - }; -} - -macro_rules! visit_place_fns { - (mut) => { - fn super_place(&mut self, place: &mut Place, ptx: PlaceContext, location: Location) { - self.visit_local(&mut place.local, ptx, location); - - for elem in place.projection.iter_mut() { - self.visit_projection_elem(elem, ptx, location); - } - } - - // We don't have to replicate the `process_projection()` like we did in - // `rustc_middle::mir::visit.rs` here because the `projection` field in `Place` - // of Stable-MIR is not an immutable borrow, unlike in `Place` of MIR. - fn visit_projection_elem( - &mut self, - elem: &mut ProjectionElem, - ptx: PlaceContext, - location: Location, - ) { - self.super_projection_elem(elem, ptx, location) - } - - fn super_projection_elem( - &mut self, - elem: &mut ProjectionElem, - ptx: PlaceContext, - location: Location, - ) { - match elem { - ProjectionElem::Deref => {} - ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location), - ProjectionElem::Index(local) => self.visit_local(local, ptx, location), - ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {} - ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} - ProjectionElem::Downcast(_idx) => {} - ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), - ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), - } - } - }; - - () => { - fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { - self.visit_local(&place.local, ptx, location); - - for (idx, elem) in place.projection.iter().enumerate() { - let place_ref = - PlaceRef { local: place.local, projection: &place.projection[..idx] }; - self.visit_projection_elem(place_ref, elem, ptx, location); - } - } - - fn visit_projection_elem<'a>( - &mut self, - place_ref: PlaceRef<'a>, - elem: &ProjectionElem, - ptx: PlaceContext, - location: Location, - ) { - let _ = place_ref; - self.super_projection_elem(elem, ptx, location); - } - - fn super_projection_elem( - &mut self, - elem: &ProjectionElem, - ptx: PlaceContext, - location: Location, - ) { - match elem { - ProjectionElem::Deref => {} - ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location), - ProjectionElem::Index(local) => self.visit_local(local, ptx, location), - ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {} - ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {} - ProjectionElem::Downcast(_idx) => {} - ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location), - ProjectionElem::Subtype(ty) => self.visit_ty(ty, location), - } - } - }; -} - -make_mir_visitor!(MirVisitor,); -make_mir_visitor!(MutMirVisitor, mut); - -/// This function is a no-op that gets used to ensure this visitor is kept up-to-date. -/// -/// The idea is that whenever we replace an Opaque type by a real type, the compiler will fail -/// when trying to invoke `visit_opaque`. -/// -/// If you are here because your compilation is broken, replace the failing call to `visit_opaque()` -/// by a `visit_` for your construct. -fn visit_opaque(_: &Opaque) {} - -/// The location of a statement / terminator in the code and the CFG. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct Location(Span); - -impl Location { - pub fn span(&self) -> Span { - self.0 - } -} - -/// Location of the statement at the given index for a given basic block. Assumes that `stmt_idx` -/// and `bb_idx` are valid for a given body. -pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location { - let bb = &body.blocks[*bb_idx]; - let stmt = &bb.statements[stmt_idx]; - Location(stmt.span) -} - -/// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given -/// body. -pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location { - let bb = &body.blocks[*bb_idx]; - let terminator = &bb.terminator; - Location(terminator.span) -} - -/// Reference to a place used to represent a partial projection. -pub struct PlaceRef<'a> { - pub local: Local, - pub projection: &'a [ProjectionElem], -} - -impl PlaceRef<'_> { - /// Get the type of this place. - pub fn ty(&self, locals: &[LocalDecl]) -> Result { - self.projection.iter().try_fold(locals[self.local].ty, |place_ty, elem| elem.ty(place_ty)) - } -} - -/// Information about a place's usage. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct PlaceContext { - /// Whether the access is mutable or not. Keep this private so we can increment the type in a - /// backward compatible manner. - is_mut: bool, -} - -impl PlaceContext { - const MUTATING: Self = PlaceContext { is_mut: true }; - const NON_MUTATING: Self = PlaceContext { is_mut: false }; - const NON_USE: Self = PlaceContext { is_mut: false }; - - pub fn is_mutating(&self) -> bool { - self.is_mut - } -} diff --git a/compiler/stable_mir/src/target.rs b/compiler/stable_mir/src/target.rs deleted file mode 100644 index 32c3a2a9122..00000000000 --- a/compiler/stable_mir/src/target.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! Provide information about the machine that this is being compiled into. - -use serde::Serialize; - -use crate::compiler_interface::with; - -/// The properties of the target machine being compiled into. -#[derive(Clone, PartialEq, Eq, Serialize)] -pub struct MachineInfo { - pub endian: Endian, - pub pointer_width: MachineSize, -} - -impl MachineInfo { - pub fn target() -> MachineInfo { - with(|cx| cx.target_info()) - } - - pub fn target_endianness() -> Endian { - with(|cx| cx.target_info().endian) - } - - pub fn target_pointer_width() -> MachineSize { - with(|cx| cx.target_info().pointer_width) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Serialize)] -pub enum Endian { - Little, - Big, -} - -/// Represent the size of a component. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] -pub struct MachineSize { - num_bits: usize, -} - -impl MachineSize { - #[inline(always)] - pub fn bytes(self) -> usize { - self.num_bits / 8 - } - - #[inline(always)] - pub fn bits(self) -> usize { - self.num_bits - } - - #[inline(always)] - pub fn from_bits(num_bits: usize) -> MachineSize { - MachineSize { num_bits } - } - - #[inline] - pub fn unsigned_int_max(self) -> Option { - (self.num_bits <= 128).then(|| u128::MAX >> (128 - self.bits())) - } -} diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs deleted file mode 100644 index 25ec4a440d6..00000000000 --- a/compiler/stable_mir/src/ty.rs +++ /dev/null @@ -1,1622 +0,0 @@ -use std::fmt::{self, Debug, Display, Formatter}; -use std::ops::Range; - -use serde::Serialize; - -use super::mir::{Body, Mutability, Safety}; -use super::{DefId, Error, Symbol, with}; -use crate::abi::{FnAbi, Layout}; -use crate::crate_def::{CrateDef, CrateDefItems, CrateDefType}; -use crate::mir::alloc::{AllocId, read_target_int, read_target_uint}; -use crate::mir::mono::StaticDef; -use crate::target::MachineInfo; -use crate::{Filename, Opaque}; - -#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] -pub struct Ty(usize); - -impl Debug for Ty { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Ty").field("id", &self.0).field("kind", &self.kind()).finish() - } -} - -/// Constructors for `Ty`. -impl Ty { - /// Create a new type from a given kind. - pub fn from_rigid_kind(kind: RigidTy) -> Ty { - with(|cx| cx.new_rigid_ty(kind)) - } - - /// Create a new array type. - pub fn try_new_array(elem_ty: Ty, size: u64) -> Result { - Ok(Ty::from_rigid_kind(RigidTy::Array(elem_ty, TyConst::try_from_target_usize(size)?))) - } - - /// Create a new array type from Const length. - pub fn new_array_with_const_len(elem_ty: Ty, len: TyConst) -> Ty { - Ty::from_rigid_kind(RigidTy::Array(elem_ty, len)) - } - - /// Create a new pointer type. - pub fn new_ptr(pointee_ty: Ty, mutability: Mutability) -> Ty { - Ty::from_rigid_kind(RigidTy::RawPtr(pointee_ty, mutability)) - } - - /// Create a new reference type. - pub fn new_ref(reg: Region, pointee_ty: Ty, mutability: Mutability) -> Ty { - Ty::from_rigid_kind(RigidTy::Ref(reg, pointee_ty, mutability)) - } - - /// Create a new pointer type. - pub fn new_tuple(tys: &[Ty]) -> Ty { - Ty::from_rigid_kind(RigidTy::Tuple(Vec::from(tys))) - } - - /// Create a new closure type. - pub fn new_closure(def: ClosureDef, args: GenericArgs) -> Ty { - Ty::from_rigid_kind(RigidTy::Closure(def, args)) - } - - /// Create a new coroutine type. - pub fn new_coroutine(def: CoroutineDef, args: GenericArgs, mov: Movability) -> Ty { - Ty::from_rigid_kind(RigidTy::Coroutine(def, args, mov)) - } - - /// Create a new closure type. - pub fn new_coroutine_closure(def: CoroutineClosureDef, args: GenericArgs) -> Ty { - Ty::from_rigid_kind(RigidTy::CoroutineClosure(def, args)) - } - - /// Create a new box type that represents `Box`, for the given inner type `T`. - pub fn new_box(inner_ty: Ty) -> Ty { - with(|cx| cx.new_box_ty(inner_ty)) - } - - /// Create a type representing `usize`. - pub fn usize_ty() -> Ty { - Ty::from_rigid_kind(RigidTy::Uint(UintTy::Usize)) - } - - /// Create a type representing `bool`. - pub fn bool_ty() -> Ty { - Ty::from_rigid_kind(RigidTy::Bool) - } - - /// Create a type representing a signed integer. - pub fn signed_ty(inner: IntTy) -> Ty { - Ty::from_rigid_kind(RigidTy::Int(inner)) - } - - /// Create a type representing an unsigned integer. - pub fn unsigned_ty(inner: UintTy) -> Ty { - Ty::from_rigid_kind(RigidTy::Uint(inner)) - } - - /// Get a type layout. - pub fn layout(self) -> Result { - with(|cx| cx.ty_layout(self)) - } -} - -impl Ty { - pub fn kind(&self) -> TyKind { - with(|context| context.ty_kind(*self)) - } -} - -/// Represents a pattern in the type system -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum Pattern { - Range { start: Option, end: Option, include_end: bool }, -} - -/// Represents a constant in the type system -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct TyConst { - pub(crate) kind: TyConstKind, - pub id: TyConstId, -} - -impl TyConst { - pub fn new(kind: TyConstKind, id: TyConstId) -> TyConst { - Self { kind, id } - } - - /// Retrieve the constant kind. - pub fn kind(&self) -> &TyConstKind { - &self.kind - } - - /// Creates an interned usize constant. - pub fn try_from_target_usize(val: u64) -> Result { - with(|cx| cx.try_new_ty_const_uint(val.into(), UintTy::Usize)) - } - - /// Try to evaluate to a target `usize`. - pub fn eval_target_usize(&self) -> Result { - with(|cx| cx.eval_target_usize_ty(self)) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum TyConstKind { - Param(ParamConst), - Bound(DebruijnIndex, BoundVar), - Unevaluated(ConstDef, GenericArgs), - - // FIXME: These should be a valtree - Value(Ty, Allocation), - ZSTValue(Ty), -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub struct TyConstId(usize); - -/// Represents a constant in MIR -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct MirConst { - /// The constant kind. - pub(crate) kind: ConstantKind, - /// The constant type. - pub(crate) ty: Ty, - /// Used for internal tracking of the internal constant. - pub id: MirConstId, -} - -impl MirConst { - /// Build a constant. Note that this should only be used by the compiler. - pub fn new(kind: ConstantKind, ty: Ty, id: MirConstId) -> MirConst { - MirConst { kind, ty, id } - } - - /// Retrieve the constant kind. - pub fn kind(&self) -> &ConstantKind { - &self.kind - } - - /// Get the constant type. - pub fn ty(&self) -> Ty { - self.ty - } - - /// Try to evaluate to a target `usize`. - pub fn eval_target_usize(&self) -> Result { - with(|cx| cx.eval_target_usize(self)) - } - - /// Create a constant that represents a new zero-sized constant of type T. - /// Fails if the type is not a ZST or if it doesn't have a known size. - pub fn try_new_zero_sized(ty: Ty) -> Result { - with(|cx| cx.try_new_const_zst(ty)) - } - - /// Build a new constant that represents the given string. - /// - /// Note that there is no guarantee today about duplication of the same constant. - /// I.e.: Calling this function multiple times with the same argument may or may not return - /// the same allocation. - pub fn from_str(value: &str) -> MirConst { - with(|cx| cx.new_const_str(value)) - } - - /// Build a new constant that represents the given boolean value. - pub fn from_bool(value: bool) -> MirConst { - with(|cx| cx.new_const_bool(value)) - } - - /// Build a new constant that represents the given unsigned integer. - pub fn try_from_uint(value: u128, uint_ty: UintTy) -> Result { - with(|cx| cx.try_new_const_uint(value, uint_ty)) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] -pub struct MirConstId(usize); - -type Ident = Opaque; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Region { - pub kind: RegionKind, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum RegionKind { - ReEarlyParam(EarlyParamRegion), - ReBound(DebruijnIndex, BoundRegion), - ReStatic, - RePlaceholder(Placeholder), - ReErased, -} - -pub(crate) type DebruijnIndex = u32; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct EarlyParamRegion { - pub index: u32, - pub name: Symbol, -} - -pub(crate) type BoundVar = u32; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct BoundRegion { - pub var: BoundVar, - pub kind: BoundRegionKind, -} - -pub(crate) type UniverseIndex = u32; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Placeholder { - pub universe: UniverseIndex, - pub bound: T, -} - -#[derive(Clone, Copy, PartialEq, Eq, Serialize)] -pub struct Span(usize); - -impl Debug for Span { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Span") - .field("id", &self.0) - .field("repr", &with(|cx| cx.span_to_string(*self))) - .finish() - } -} - -impl Span { - /// Return filename for diagnostic purposes - pub fn get_filename(&self) -> Filename { - with(|c| c.get_filename(self)) - } - - /// Return lines that correspond to this `Span` - pub fn get_lines(&self) -> LineInfo { - with(|c| c.get_lines(self)) - } - - /// Return the span location to be printed in diagnostic messages. - /// - /// This may leak local file paths and should not be used to build artifacts that may be - /// distributed. - pub fn diagnostic(&self) -> String { - with(|c| c.span_to_string(*self)) - } -} - -#[derive(Clone, Copy, Debug, Serialize)] -/// Information you get from `Span` in a struct form. -/// Line and col start from 1. -pub struct LineInfo { - pub start_line: usize, - pub start_col: usize, - pub end_line: usize, - pub end_col: usize, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum TyKind { - RigidTy(RigidTy), - Alias(AliasKind, AliasTy), - Param(ParamTy), - Bound(usize, BoundTy), -} - -impl TyKind { - pub fn rigid(&self) -> Option<&RigidTy> { - if let TyKind::RigidTy(inner) = self { Some(inner) } else { None } - } - - #[inline] - pub fn is_unit(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Tuple(data)) if data.is_empty()) - } - - #[inline] - pub fn is_bool(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Bool)) - } - - #[inline] - pub fn is_char(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Char)) - } - - #[inline] - pub fn is_trait(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _, DynKind::Dyn))) - } - - #[inline] - pub fn is_enum(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Enum) - } - - #[inline] - pub fn is_struct(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Struct) - } - - #[inline] - pub fn is_union(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.kind() == AdtKind::Union) - } - - #[inline] - pub fn is_adt(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Adt(..))) - } - - #[inline] - pub fn is_ref(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Ref(..))) - } - - #[inline] - pub fn is_fn(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::FnDef(..))) - } - - #[inline] - pub fn is_fn_ptr(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::FnPtr(..))) - } - - #[inline] - pub fn is_primitive(&self) -> bool { - matches!( - self, - TyKind::RigidTy( - RigidTy::Bool - | RigidTy::Char - | RigidTy::Int(_) - | RigidTy::Uint(_) - | RigidTy::Float(_) - ) - ) - } - - #[inline] - pub fn is_float(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Float(_))) - } - - #[inline] - pub fn is_integral(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Int(_) | RigidTy::Uint(_))) - } - - #[inline] - pub fn is_numeric(&self) -> bool { - self.is_integral() || self.is_float() - } - - #[inline] - pub fn is_signed(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Int(_))) - } - - #[inline] - pub fn is_str(&self) -> bool { - *self == TyKind::RigidTy(RigidTy::Str) - } - - #[inline] - pub fn is_cstr(&self) -> bool { - let TyKind::RigidTy(RigidTy::Adt(def, _)) = self else { - return false; - }; - with(|cx| cx.adt_is_cstr(*def)) - } - - #[inline] - pub fn is_slice(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Slice(_))) - } - - #[inline] - pub fn is_array(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Array(..))) - } - - #[inline] - pub fn is_mutable_ptr(&self) -> bool { - matches!( - self, - TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) - | TyKind::RigidTy(RigidTy::Ref(_, _, Mutability::Mut)) - ) - } - - #[inline] - pub fn is_raw_ptr(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::RawPtr(..))) - } - - /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). - #[inline] - pub fn is_any_ptr(&self) -> bool { - self.is_ref() || self.is_raw_ptr() || self.is_fn_ptr() - } - - #[inline] - pub fn is_coroutine(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Coroutine(..))) - } - - #[inline] - pub fn is_closure(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Closure(..))) - } - - #[inline] - pub fn is_box(&self) -> bool { - match self { - TyKind::RigidTy(RigidTy::Adt(def, _)) => def.is_box(), - _ => false, - } - } - - #[inline] - pub fn is_simd(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Adt(def, _)) if def.is_simd()) - } - - pub fn trait_principal(&self) -> Option> { - if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _, _)) = self { - if let Some(Binder { value: ExistentialPredicate::Trait(trait_ref), bound_vars }) = - predicates.first() - { - Some(Binder { value: trait_ref.clone(), bound_vars: bound_vars.clone() }) - } else { - None - } - } else { - None - } - } - - /// Returns the type of `ty[i]` for builtin types. - pub fn builtin_index(&self) -> Option { - match self.rigid()? { - RigidTy::Array(ty, _) | RigidTy::Slice(ty) => Some(*ty), - _ => None, - } - } - - /// Returns the type and mutability of `*ty` for builtin types. - /// - /// The parameter `explicit` indicates if this is an *explicit* dereference. - /// Some types -- notably raw ptrs -- can only be dereferenced explicitly. - pub fn builtin_deref(&self, explicit: bool) -> Option { - match self.rigid()? { - RigidTy::Adt(def, args) if def.is_box() => { - Some(TypeAndMut { ty: *args.0.first()?.ty()?, mutability: Mutability::Not }) - } - RigidTy::Ref(_, ty, mutability) => { - Some(TypeAndMut { ty: *ty, mutability: *mutability }) - } - RigidTy::RawPtr(ty, mutability) if explicit => { - Some(TypeAndMut { ty: *ty, mutability: *mutability }) - } - _ => None, - } - } - - /// Get the function signature for function like types (Fn, FnPtr, and Closure) - pub fn fn_sig(&self) -> Option { - match self { - TyKind::RigidTy(RigidTy::FnDef(def, args)) => Some(with(|cx| cx.fn_sig(*def, args))), - TyKind::RigidTy(RigidTy::FnPtr(sig)) => Some(sig.clone()), - TyKind::RigidTy(RigidTy::Closure(_def, args)) => Some(with(|cx| cx.closure_sig(args))), - _ => None, - } - } - - /// Get the discriminant type for this type. - pub fn discriminant_ty(&self) -> Option { - self.rigid().map(|ty| with(|cx| cx.rigid_ty_discriminant_ty(ty))) - } - - /// Deconstruct a function type if this is one. - pub fn fn_def(&self) -> Option<(FnDef, &GenericArgs)> { - if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = self { - Some((*def, args)) - } else { - None - } - } -} - -pub struct TypeAndMut { - pub ty: Ty, - pub mutability: Mutability, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum RigidTy { - Bool, - Char, - Int(IntTy), - Uint(UintTy), - Float(FloatTy), - Adt(AdtDef, GenericArgs), - Foreign(ForeignDef), - Str, - Array(Ty, TyConst), - Pat(Ty, Pattern), - Slice(Ty), - RawPtr(Ty, Mutability), - Ref(Region, Ty, Mutability), - FnDef(FnDef, GenericArgs), - FnPtr(PolyFnSig), - Closure(ClosureDef, GenericArgs), - // FIXME(stable_mir): Movability here is redundant - Coroutine(CoroutineDef, GenericArgs, Movability), - CoroutineClosure(CoroutineClosureDef, GenericArgs), - Dynamic(Vec>, Region, DynKind), - Never, - Tuple(Vec), - CoroutineWitness(CoroutineWitnessDef, GenericArgs), -} - -impl RigidTy { - /// Get the discriminant type for this type. - pub fn discriminant_ty(&self) -> Ty { - with(|cx| cx.rigid_ty_discriminant_ty(self)) - } -} - -impl From for TyKind { - fn from(value: RigidTy) -> Self { - TyKind::RigidTy(value) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] -pub enum IntTy { - Isize, - I8, - I16, - I32, - I64, - I128, -} - -impl IntTy { - pub fn num_bytes(self) -> usize { - match self { - IntTy::Isize => crate::target::MachineInfo::target_pointer_width().bytes(), - IntTy::I8 => 1, - IntTy::I16 => 2, - IntTy::I32 => 4, - IntTy::I64 => 8, - IntTy::I128 => 16, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] -pub enum UintTy { - Usize, - U8, - U16, - U32, - U64, - U128, -} - -impl UintTy { - pub fn num_bytes(self) -> usize { - match self { - UintTy::Usize => crate::target::MachineInfo::target_pointer_width().bytes(), - UintTy::U8 => 1, - UintTy::U16 => 2, - UintTy::U32 => 4, - UintTy::U64 => 8, - UintTy::U128 => 16, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] -pub enum FloatTy { - F16, - F32, - F64, - F128, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] -pub enum Movability { - Static, - Movable, -} - -crate_def! { - #[derive(Serialize)] - pub ForeignModuleDef; -} - -impl ForeignModuleDef { - pub fn module(&self) -> ForeignModule { - with(|cx| cx.foreign_module(*self)) - } -} - -pub struct ForeignModule { - pub def_id: ForeignModuleDef, - pub abi: Abi, -} - -impl ForeignModule { - pub fn items(&self) -> Vec { - with(|cx| cx.foreign_items(self.def_id)) - } -} - -crate_def_with_ty! { - /// Hold information about a ForeignItem in a crate. - #[derive(Serialize)] - pub ForeignDef; -} - -impl ForeignDef { - pub fn kind(&self) -> ForeignItemKind { - with(|cx| cx.foreign_item_kind(*self)) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] -pub enum ForeignItemKind { - Fn(FnDef), - Static(StaticDef), - Type(Ty), -} - -crate_def_with_ty! { - /// Hold information about a function definition in a crate. - #[derive(Serialize)] - pub FnDef; -} - -impl FnDef { - // Get the function body if available. - pub fn body(&self) -> Option { - with(|ctx| ctx.has_body(self.0).then(|| ctx.mir_body(self.0))) - } - - // Check if the function body is available. - pub fn has_body(&self) -> bool { - with(|ctx| ctx.has_body(self.0)) - } - - /// Get the information of the intrinsic if this function is a definition of one. - pub fn as_intrinsic(&self) -> Option { - with(|cx| cx.intrinsic(self.def_id())) - } - - /// Check if the function is an intrinsic. - #[inline] - pub fn is_intrinsic(&self) -> bool { - self.as_intrinsic().is_some() - } - - /// Get the function signature for this function definition. - pub fn fn_sig(&self) -> PolyFnSig { - let kind = self.ty().kind(); - kind.fn_sig().unwrap() - } -} - -crate_def_with_ty! { - #[derive(Serialize)] - pub IntrinsicDef; -} - -impl IntrinsicDef { - /// Returns the plain name of the intrinsic. - /// e.g., `transmute` for `core::intrinsics::transmute`. - pub fn fn_name(&self) -> Symbol { - with(|cx| cx.intrinsic_name(*self)) - } - - /// Returns whether the intrinsic has no meaningful body and all backends - /// need to shim all calls to it. - pub fn must_be_overridden(&self) -> bool { - with(|cx| !cx.has_body(self.0)) - } -} - -impl From for FnDef { - fn from(def: IntrinsicDef) -> Self { - FnDef(def.0) - } -} - -crate_def! { - #[derive(Serialize)] - pub ClosureDef; -} - -crate_def! { - #[derive(Serialize)] - pub CoroutineDef; -} - -crate_def! { - #[derive(Serialize)] - pub CoroutineClosureDef; -} - -crate_def! { - #[derive(Serialize)] - pub ParamDef; -} - -crate_def! { - #[derive(Serialize)] - pub BrNamedDef; -} - -crate_def! { - #[derive(Serialize)] - pub AdtDef; -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] -pub enum AdtKind { - Enum, - Union, - Struct, -} - -impl AdtDef { - pub fn kind(&self) -> AdtKind { - with(|cx| cx.adt_kind(*self)) - } - - /// Retrieve the type of this Adt. - pub fn ty(&self) -> Ty { - with(|cx| cx.def_ty(self.0)) - } - - /// Retrieve the type of this Adt by instantiating and normalizing it with the given arguments. - /// - /// This will assume the type can be instantiated with these arguments. - pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { - with(|cx| cx.def_ty_with_args(self.0, args)) - } - - pub fn is_box(&self) -> bool { - with(|cx| cx.adt_is_box(*self)) - } - - pub fn is_simd(&self) -> bool { - with(|cx| cx.adt_is_simd(*self)) - } - - /// The number of variants in this ADT. - pub fn num_variants(&self) -> usize { - with(|cx| cx.adt_variants_len(*self)) - } - - /// Retrieve the variants in this ADT. - pub fn variants(&self) -> Vec { - self.variants_iter().collect() - } - - /// Iterate over the variants in this ADT. - pub fn variants_iter(&self) -> impl Iterator { - (0..self.num_variants()) - .map(|idx| VariantDef { idx: VariantIdx::to_val(idx), adt_def: *self }) - } - - pub fn variant(&self, idx: VariantIdx) -> Option { - (idx.to_index() < self.num_variants()).then_some(VariantDef { idx, adt_def: *self }) - } -} - -/// Definition of a variant, which can be either a struct / union field or an enum variant. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct VariantDef { - /// The variant index. - /// - /// ## Warning - /// Do not access this field directly! - pub idx: VariantIdx, - /// The data type where this variant comes from. - /// For now, we use this to retrieve information about the variant itself so we don't need to - /// cache more information. - /// - /// ## Warning - /// Do not access this field directly! - pub adt_def: AdtDef, -} - -impl VariantDef { - pub fn name(&self) -> Symbol { - with(|cx| cx.variant_name(*self)) - } - - /// Retrieve all the fields in this variant. - // We expect user to cache this and use it directly since today it is expensive to generate all - // fields name. - pub fn fields(&self) -> Vec { - with(|cx| cx.variant_fields(*self)) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct FieldDef { - /// The field definition. - /// - /// ## Warning - /// Do not access this field directly! This is public for the compiler to have access to it. - pub def: DefId, - - /// The field name. - pub name: Symbol, -} - -impl FieldDef { - /// Retrieve the type of this field instantiating and normalizing it with the given arguments. - /// - /// This will assume the type can be instantiated with these arguments. - pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { - with(|cx| cx.def_ty_with_args(self.def, args)) - } - - /// Retrieve the type of this field. - pub fn ty(&self) -> Ty { - with(|cx| cx.def_ty(self.def)) - } -} - -impl Display for AdtKind { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_str(match self { - AdtKind::Enum => "enum", - AdtKind::Union => "union", - AdtKind::Struct => "struct", - }) - } -} - -impl AdtKind { - pub fn is_enum(&self) -> bool { - matches!(self, AdtKind::Enum) - } - - pub fn is_struct(&self) -> bool { - matches!(self, AdtKind::Struct) - } - - pub fn is_union(&self) -> bool { - matches!(self, AdtKind::Union) - } -} - -crate_def! { - #[derive(Serialize)] - pub AliasDef; -} - -crate_def! { - /// A trait's definition. - #[derive(Serialize)] - pub TraitDef; -} - -impl_crate_def_items! { - TraitDef; -} - -impl TraitDef { - pub fn declaration(trait_def: &TraitDef) -> TraitDecl { - with(|cx| cx.trait_decl(trait_def)) - } -} - -crate_def! { - #[derive(Serialize)] - pub GenericDef; -} - -crate_def_with_ty! { - #[derive(Serialize)] - pub ConstDef; -} - -crate_def! { - /// A trait impl definition. - #[derive(Serialize)] - pub ImplDef; -} - -impl_crate_def_items! { - ImplDef; -} - -impl ImplDef { - /// Retrieve information about this implementation. - pub fn trait_impl(&self) -> ImplTrait { - with(|cx| cx.trait_impl(self)) - } -} - -crate_def! { - #[derive(Serialize)] - pub RegionDef; -} - -crate_def! { - #[derive(Serialize)] - pub CoroutineWitnessDef; -} - -/// A list of generic arguments. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct GenericArgs(pub Vec); - -impl std::ops::Index for GenericArgs { - type Output = Ty; - - fn index(&self, index: ParamTy) -> &Self::Output { - self.0[index.index as usize].expect_ty() - } -} - -impl std::ops::Index for GenericArgs { - type Output = TyConst; - - fn index(&self, index: ParamConst) -> &Self::Output { - self.0[index.index as usize].expect_const() - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum GenericArgKind { - Lifetime(Region), - Type(Ty), - Const(TyConst), -} - -impl GenericArgKind { - /// Panic if this generic argument is not a type, otherwise - /// return the type. - #[track_caller] - pub fn expect_ty(&self) -> &Ty { - match self { - GenericArgKind::Type(ty) => ty, - _ => panic!("{self:?}"), - } - } - - /// Panic if this generic argument is not a const, otherwise - /// return the const. - #[track_caller] - pub fn expect_const(&self) -> &TyConst { - match self { - GenericArgKind::Const(c) => c, - _ => panic!("{self:?}"), - } - } - - /// Return the generic argument type if applicable, otherwise return `None`. - pub fn ty(&self) -> Option<&Ty> { - match self { - GenericArgKind::Type(ty) => Some(ty), - _ => None, - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum TermKind { - Type(Ty), - Const(TyConst), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AliasKind { - Projection, - Inherent, - Opaque, - Weak, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct AliasTy { - pub def_id: AliasDef, - pub args: GenericArgs, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct AliasTerm { - pub def_id: AliasDef, - pub args: GenericArgs, -} - -pub type PolyFnSig = Binder; - -impl PolyFnSig { - /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. - /// - /// NB: this doesn't handle virtual calls - those should use `Instance::fn_abi` - /// instead, where the instance is an `InstanceKind::Virtual`. - pub fn fn_ptr_abi(self) -> Result { - with(|cx| cx.fn_ptr_abi(self)) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct FnSig { - pub inputs_and_output: Vec, - pub c_variadic: bool, - pub safety: Safety, - pub abi: Abi, -} - -impl FnSig { - pub fn output(&self) -> Ty { - self.inputs_and_output[self.inputs_and_output.len() - 1] - } - - pub fn inputs(&self) -> &[Ty] { - &self.inputs_and_output[..self.inputs_and_output.len() - 1] - } -} - -#[derive(Clone, PartialEq, Eq, Debug, Serialize)] -pub enum Abi { - Rust, - C { unwind: bool }, - Cdecl { unwind: bool }, - Stdcall { unwind: bool }, - Fastcall { unwind: bool }, - Vectorcall { unwind: bool }, - Thiscall { unwind: bool }, - Aapcs { unwind: bool }, - Win64 { unwind: bool }, - SysV64 { unwind: bool }, - PtxKernel, - Msp430Interrupt, - X86Interrupt, - GpuKernel, - EfiApi, - AvrInterrupt, - AvrNonBlockingInterrupt, - CCmseNonSecureCall, - CCmseNonSecureEntry, - System { unwind: bool }, - RustIntrinsic, - RustCall, - Unadjusted, - RustCold, - RiscvInterruptM, - RiscvInterruptS, -} - -/// A binder represents a possibly generic type and its bound vars. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Binder { - pub value: T, - pub bound_vars: Vec, -} - -impl Binder { - /// Create a new binder with the given bound vars. - pub fn bind_with_vars(value: T, bound_vars: Vec) -> Self { - Binder { value, bound_vars } - } - - /// Create a new binder with no bounded variable. - pub fn dummy(value: T) -> Self { - Binder { value, bound_vars: vec![] } - } - - pub fn skip_binder(self) -> T { - self.value - } - - pub fn map_bound_ref(&self, f: F) -> Binder - where - F: FnOnce(&T) -> U, - { - let Binder { value, bound_vars } = self; - let new_value = f(value); - Binder { value: new_value, bound_vars: bound_vars.clone() } - } - - pub fn map_bound(self, f: F) -> Binder - where - F: FnOnce(T) -> U, - { - let Binder { value, bound_vars } = self; - let new_value = f(value); - Binder { value: new_value, bound_vars } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct EarlyBinder { - pub value: T, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum BoundVariableKind { - Ty(BoundTyKind), - Region(BoundRegionKind), - Const, -} - -#[derive(Clone, PartialEq, Eq, Debug, Serialize)] -pub enum BoundTyKind { - Anon, - Param(ParamDef, String), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum BoundRegionKind { - BrAnon, - BrNamed(BrNamedDef, String), - BrEnv, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum DynKind { - Dyn, - DynStar, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum ExistentialPredicate { - Trait(ExistentialTraitRef), - Projection(ExistentialProjection), - AutoTrait(TraitDef), -} - -/// An existential reference to a trait where `Self` is not included. -/// -/// The `generic_args` will include any other known argument. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ExistentialTraitRef { - pub def_id: TraitDef, - pub generic_args: GenericArgs, -} - -impl Binder { - pub fn with_self_ty(&self, self_ty: Ty) -> Binder { - self.map_bound_ref(|trait_ref| trait_ref.with_self_ty(self_ty)) - } -} - -impl ExistentialTraitRef { - pub fn with_self_ty(&self, self_ty: Ty) -> TraitRef { - TraitRef::new(self.def_id, self_ty, &self.generic_args) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ExistentialProjection { - pub def_id: TraitDef, - pub generic_args: GenericArgs, - pub term: TermKind, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ParamTy { - pub index: u32, - pub name: String, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct BoundTy { - pub var: usize, - pub kind: BoundTyKind, -} - -pub type Bytes = Vec>; - -/// Size in bytes. -pub type Size = usize; - -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] -pub struct Prov(pub AllocId); - -pub type Align = u64; -pub type Promoted = u32; -pub type InitMaskMaterialized = Vec; - -/// Stores the provenance information of pointers stored in memory. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)] -pub struct ProvenanceMap { - /// Provenance in this map applies from the given offset for an entire pointer-size worth of - /// bytes. Two entries in this map are always at least a pointer size apart. - pub ptrs: Vec<(Size, Prov)>, -} - -#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)] -pub struct Allocation { - pub bytes: Bytes, - pub provenance: ProvenanceMap, - pub align: Align, - pub mutability: Mutability, -} - -impl Allocation { - /// Get a vector of bytes for an Allocation that has been fully initialized - pub fn raw_bytes(&self) -> Result, Error> { - self.bytes - .iter() - .copied() - .collect::>>() - .ok_or_else(|| error!("Found uninitialized bytes: `{:?}`", self.bytes)) - } - - /// Read a uint value from the specified range. - pub fn read_partial_uint(&self, range: Range) -> Result { - if range.end - range.start > 16 { - return Err(error!("Allocation is bigger than largest integer")); - } - if range.end > self.bytes.len() { - return Err(error!( - "Range is out of bounds. Allocation length is `{}`, but requested range `{:?}`", - self.bytes.len(), - range - )); - } - let raw = self.bytes[range] - .iter() - .copied() - .collect::>>() - .ok_or_else(|| error!("Found uninitialized bytes: `{:?}`", self.bytes))?; - read_target_uint(&raw) - } - - /// Read this allocation and try to convert it to an unassigned integer. - pub fn read_uint(&self) -> Result { - if self.bytes.len() > 16 { - return Err(error!("Allocation is bigger than largest integer")); - } - let raw = self.raw_bytes()?; - read_target_uint(&raw) - } - - /// Read this allocation and try to convert it to a signed integer. - pub fn read_int(&self) -> Result { - if self.bytes.len() > 16 { - return Err(error!("Allocation is bigger than largest integer")); - } - let raw = self.raw_bytes()?; - read_target_int(&raw) - } - - /// Read this allocation and try to convert it to a boolean. - pub fn read_bool(&self) -> Result { - match self.read_int()? { - 0 => Ok(false), - 1 => Ok(true), - val => Err(error!("Unexpected value for bool: `{val}`")), - } - } - - /// Read this allocation as a pointer and return whether it represents a `null` pointer. - pub fn is_null(&self) -> Result { - let len = self.bytes.len(); - let ptr_len = MachineInfo::target_pointer_width().bytes(); - if len != ptr_len { - return Err(error!("Expected width of pointer (`{ptr_len}`), but found: `{len}`")); - } - Ok(self.read_uint()? == 0 && self.provenance.ptrs.is_empty()) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum ConstantKind { - Ty(TyConst), - Allocated(Allocation), - Unevaluated(UnevaluatedConst), - Param(ParamConst), - /// Store ZST constants. - /// We have to special handle these constants since its type might be generic. - ZeroSized, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ParamConst { - pub index: u32, - pub name: String, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct UnevaluatedConst { - pub def: ConstDef, - pub args: GenericArgs, - pub promoted: Option, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] -pub enum TraitSpecializationKind { - None, - Marker, - AlwaysApplicable, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct TraitDecl { - pub def_id: TraitDef, - pub safety: Safety, - pub paren_sugar: bool, - pub has_auto_impl: bool, - pub is_marker: bool, - pub is_coinductive: bool, - pub skip_array_during_method_dispatch: bool, - pub skip_boxed_slice_during_method_dispatch: bool, - pub specialization_kind: TraitSpecializationKind, - pub must_implement_one_of: Option>, - pub implement_via_object: bool, - pub deny_explicit_impl: bool, -} - -impl TraitDecl { - pub fn generics_of(&self) -> Generics { - with(|cx| cx.generics_of(self.def_id.0)) - } - - pub fn predicates_of(&self) -> GenericPredicates { - with(|cx| cx.predicates_of(self.def_id.0)) - } - - pub fn explicit_predicates_of(&self) -> GenericPredicates { - with(|cx| cx.explicit_predicates_of(self.def_id.0)) - } -} - -pub type ImplTrait = EarlyBinder; - -/// A complete reference to a trait, i.e., one where `Self` is known. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct TraitRef { - pub def_id: TraitDef, - /// The generic arguments for this definition. - /// The first element must always be type, and it represents `Self`. - args: GenericArgs, -} - -impl TraitRef { - pub fn new(def_id: TraitDef, self_ty: Ty, gen_args: &GenericArgs) -> TraitRef { - let mut args = vec![GenericArgKind::Type(self_ty)]; - args.extend_from_slice(&gen_args.0); - TraitRef { def_id, args: GenericArgs(args) } - } - - pub fn try_new(def_id: TraitDef, args: GenericArgs) -> Result { - match &args.0[..] { - [GenericArgKind::Type(_), ..] => Ok(TraitRef { def_id, args }), - _ => Err(()), - } - } - - pub fn args(&self) -> &GenericArgs { - &self.args - } - - pub fn self_ty(&self) -> Ty { - let GenericArgKind::Type(self_ty) = self.args.0[0] else { - panic!("Self must be a type, but found: {:?}", self.args.0[0]) - }; - self_ty - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Generics { - pub parent: Option, - pub parent_count: usize, - pub params: Vec, - pub param_def_id_to_index: Vec<(GenericDef, u32)>, - pub has_self: bool, - pub has_late_bound_regions: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum GenericParamDefKind { - Lifetime, - Type { has_default: bool, synthetic: bool }, - Const { has_default: bool }, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct GenericParamDef { - pub name: super::Symbol, - pub def_id: GenericDef, - pub index: u32, - pub pure_wrt_drop: bool, - pub kind: GenericParamDefKind, -} - -pub struct GenericPredicates { - pub parent: Option, - pub predicates: Vec<(PredicateKind, Span)>, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum PredicateKind { - Clause(ClauseKind), - DynCompatible(TraitDef), - SubType(SubtypePredicate), - Coerce(CoercePredicate), - ConstEquate(TyConst, TyConst), - Ambiguous, - AliasRelate(TermKind, TermKind, AliasRelationDirection), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum ClauseKind { - Trait(TraitPredicate), - RegionOutlives(RegionOutlivesPredicate), - TypeOutlives(TypeOutlivesPredicate), - Projection(ProjectionPredicate), - ConstArgHasType(TyConst, Ty), - WellFormed(GenericArgKind), - ConstEvaluatable(TyConst), -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum ClosureKind { - Fn, - FnMut, - FnOnce, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct SubtypePredicate { - pub a: Ty, - pub b: Ty, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct CoercePredicate { - pub a: Ty, - pub b: Ty, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AliasRelationDirection { - Equate, - Subtype, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct TraitPredicate { - pub trait_ref: TraitRef, - pub polarity: PredicatePolarity, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct OutlivesPredicate(pub A, pub B); - -pub type RegionOutlivesPredicate = OutlivesPredicate; -pub type TypeOutlivesPredicate = OutlivesPredicate; - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ProjectionPredicate { - pub projection_term: AliasTerm, - pub term: TermKind, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum ImplPolarity { - Positive, - Negative, - Reservation, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum PredicatePolarity { - Positive, - Negative, -} - -pub trait IndexedVal { - fn to_val(index: usize) -> Self; - - fn to_index(&self) -> usize; -} - -macro_rules! index_impl { - ($name:ident) => { - impl IndexedVal for $name { - fn to_val(index: usize) -> Self { - $name(index) - } - fn to_index(&self) -> usize { - self.0 - } - } - }; -} - -index_impl!(TyConstId); -index_impl!(MirConstId); -index_impl!(Ty); -index_impl!(Span); - -/// The source-order index of a variant in a type. -/// -/// For example, in the following types, -/// ```ignore(illustrative) -/// enum Demo1 { -/// Variant0 { a: bool, b: i32 }, -/// Variant1 { c: u8, d: u64 }, -/// } -/// struct Demo2 { e: u8, f: u16, g: u8 } -/// ``` -/// `a` is in the variant with the `VariantIdx` of `0`, -/// `c` is in the variant with the `VariantIdx` of `1`, and -/// `g` is in the variant with the `VariantIdx` of `0`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)] -pub struct VariantIdx(usize); - -index_impl!(VariantIdx); - -crate_def! { - /// Hold infomation about an Opaque definition, particularly useful in `RPITIT`. - #[derive(Serialize)] - pub OpaqueDef; -} - -crate_def! { - #[derive(Serialize)] - pub AssocDef; -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct AssocItem { - pub def_id: AssocDef, - pub name: Symbol, - pub kind: AssocKind, - pub container: AssocItemContainer, - - /// If this is an item in an impl of a trait then this is the `DefId` of - /// the associated item on the trait that this implements. - pub trait_item_def_id: Option, - - /// Whether this is a method with an explicit self - /// as its first parameter, allowing method calls. - pub fn_has_self_parameter: bool, - - /// `Some` if the associated item (an associated type) comes from the - /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData` - /// provides additional information about its source. - pub opt_rpitit_info: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AssocKind { - Const, - Fn, - Type, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AssocItemContainer { - Trait, - Impl, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] -pub enum ImplTraitInTraitData { - Trait { fn_def_id: FnDef, opaque_def_id: OpaqueDef }, - Impl { fn_def_id: FnDef }, -} - -impl AssocItem { - pub fn is_impl_trait_in_trait(&self) -> bool { - self.opt_rpitit_info.is_some() - } -} diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs deleted file mode 100644 index 8463174f9a4..00000000000 --- a/compiler/stable_mir/src/visitor.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::ops::ControlFlow; - -use super::ty::{ - Allocation, Binder, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs, - MirConst, Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst, -}; -use crate::Opaque; -use crate::ty::TyConst; - -pub trait Visitor: Sized { - type Break; - fn visit_ty(&mut self, ty: &Ty) -> ControlFlow { - ty.super_visit(self) - } - fn visit_const(&mut self, c: &TyConst) -> ControlFlow { - c.super_visit(self) - } - fn visit_reg(&mut self, reg: &Region) -> ControlFlow { - reg.super_visit(self) - } -} - -pub trait Visitable { - fn visit(&self, visitor: &mut V) -> ControlFlow { - self.super_visit(visitor) - } - fn super_visit(&self, visitor: &mut V) -> ControlFlow; -} - -impl Visitable for Ty { - fn visit(&self, visitor: &mut V) -> ControlFlow { - visitor.visit_ty(self) - } - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match self.kind() { - super::ty::TyKind::RigidTy(ty) => ty.visit(visitor)?, - super::ty::TyKind::Alias(_, alias) => alias.args.visit(visitor)?, - super::ty::TyKind::Param(_) | super::ty::TyKind::Bound(_, _) => {} - } - ControlFlow::Continue(()) - } -} - -impl Visitable for TyConst { - fn visit(&self, visitor: &mut V) -> ControlFlow { - visitor.visit_const(self) - } - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match &self.kind { - crate::ty::TyConstKind::Param(_) | crate::ty::TyConstKind::Bound(_, _) => {} - crate::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?, - crate::ty::TyConstKind::Value(ty, alloc) => { - alloc.visit(visitor)?; - ty.visit(visitor)?; - } - crate::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?, - } - ControlFlow::Continue(()) - } -} - -impl Visitable for MirConst { - fn visit(&self, visitor: &mut V) -> ControlFlow { - self.super_visit(visitor) - } - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match &self.kind() { - super::ty::ConstantKind::Ty(ct) => ct.visit(visitor)?, - super::ty::ConstantKind::Allocated(alloc) => alloc.visit(visitor)?, - super::ty::ConstantKind::Unevaluated(uv) => uv.visit(visitor)?, - super::ty::ConstantKind::Param(_) | super::ty::ConstantKind::ZeroSized => {} - } - self.ty().visit(visitor) - } -} - -impl Visitable for Opaque { - fn super_visit(&self, _visitor: &mut V) -> ControlFlow { - ControlFlow::Continue(()) - } -} - -impl Visitable for Allocation { - fn super_visit(&self, _visitor: &mut V) -> ControlFlow { - ControlFlow::Continue(()) - } -} - -impl Visitable for UnevaluatedConst { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - let UnevaluatedConst { def, args, promoted } = self; - def.visit(visitor)?; - args.visit(visitor)?; - promoted.visit(visitor) - } -} - -impl Visitable for ConstDef { - fn super_visit(&self, _visitor: &mut V) -> ControlFlow { - ControlFlow::Continue(()) - } -} - -impl Visitable for Option { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match self { - Some(val) => val.visit(visitor), - None => ControlFlow::Continue(()), - } - } -} - -impl Visitable for Promoted { - fn super_visit(&self, _visitor: &mut V) -> ControlFlow { - ControlFlow::Continue(()) - } -} - -impl Visitable for GenericArgs { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - self.0.visit(visitor) - } -} - -impl Visitable for Region { - fn visit(&self, visitor: &mut V) -> ControlFlow { - visitor.visit_reg(self) - } - - fn super_visit(&self, _: &mut V) -> ControlFlow { - ControlFlow::Continue(()) - } -} - -impl Visitable for GenericArgKind { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match self { - GenericArgKind::Lifetime(lt) => lt.visit(visitor), - GenericArgKind::Type(t) => t.visit(visitor), - GenericArgKind::Const(c) => c.visit(visitor), - } - } -} - -impl Visitable for RigidTy { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match self { - RigidTy::Bool - | RigidTy::Char - | RigidTy::Int(_) - | RigidTy::Uint(_) - | RigidTy::Float(_) - | RigidTy::Never - | RigidTy::Foreign(_) - | RigidTy::Str => ControlFlow::Continue(()), - RigidTy::Array(t, c) => { - t.visit(visitor)?; - c.visit(visitor) - } - RigidTy::Pat(t, _p) => t.visit(visitor), - RigidTy::Slice(inner) => inner.visit(visitor), - RigidTy::RawPtr(ty, _) => ty.visit(visitor), - RigidTy::Ref(reg, ty, _) => { - reg.visit(visitor)?; - ty.visit(visitor) - } - RigidTy::Adt(_, args) - | RigidTy::Closure(_, args) - | RigidTy::Coroutine(_, args, _) - | RigidTy::CoroutineWitness(_, args) - | RigidTy::CoroutineClosure(_, args) - | RigidTy::FnDef(_, args) => args.visit(visitor), - RigidTy::FnPtr(sig) => sig.visit(visitor), - RigidTy::Dynamic(pred, r, _) => { - pred.visit(visitor)?; - r.visit(visitor) - } - RigidTy::Tuple(fields) => fields.visit(visitor), - } - } -} - -impl Visitable for Vec { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - for arg in self { - arg.visit(visitor)?; - } - ControlFlow::Continue(()) - } -} - -impl Visitable for Binder { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - self.value.visit(visitor) - } -} - -impl Visitable for ExistentialPredicate { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match self { - ExistentialPredicate::Trait(tr) => tr.generic_args.visit(visitor), - ExistentialPredicate::Projection(p) => { - p.term.visit(visitor)?; - p.generic_args.visit(visitor) - } - ExistentialPredicate::AutoTrait(_) => ControlFlow::Continue(()), - } - } -} - -impl Visitable for TermKind { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match self { - TermKind::Type(t) => t.visit(visitor), - TermKind::Const(c) => c.visit(visitor), - } - } -} - -impl Visitable for FnSig { - fn super_visit(&self, visitor: &mut V) -> ControlFlow { - self.inputs_and_output.visit(visitor) - } -} -- cgit 1.4.1-3-g733a5 From 89d0e7c03391efda88f4736f3f04f4c5a8ae71d3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 5 Apr 2025 16:16:50 +0000 Subject: Fix ProvenVia for global where clauses --- compiler/rustc_next_trait_solver/src/solve/trait_goals.rs | 12 ++++++++++-- tests/ui/codegen/mono-impossible-drop.rs | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index c46169a26c1..d42c9980f46 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1301,7 +1301,6 @@ where .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) .map(|c| c.result) .collect(); - return if let Some(response) = self.try_merge_responses(&where_bounds) { Ok((response, Some(TraitGoalProvenVia::ParamEnv))) } else { @@ -1322,9 +1321,18 @@ where }; } + // If there are *only* global where bounds, then make sure to return that this + // is still reported as being proven-via the param-env so that rigid projections + // operate correctly. + let proven_via = + if candidates.iter().all(|c| matches!(c.source, CandidateSource::ParamEnv(_))) { + TraitGoalProvenVia::ParamEnv + } else { + TraitGoalProvenVia::Misc + }; let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect(); if let Some(response) = self.try_merge_responses(&all_candidates) { - Ok((response, Some(TraitGoalProvenVia::Misc))) + Ok((response, Some(proven_via))) } else { self.flounder(&all_candidates).map(|r| (r, None)) } diff --git a/tests/ui/codegen/mono-impossible-drop.rs b/tests/ui/codegen/mono-impossible-drop.rs index dec013cfe54..c8a9554da43 100644 --- a/tests/ui/codegen/mono-impossible-drop.rs +++ b/tests/ui/codegen/mono-impossible-drop.rs @@ -1,3 +1,6 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver //@ compile-flags: -Clink-dead-code=on --crate-type=lib //@ build-pass -- cgit 1.4.1-3-g733a5