summaryrefslogtreecommitdiffstats
path: root/compiler/stable_mir
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /compiler/stable_mir
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/stable_mir')
-rw-r--r--compiler/stable_mir/Cargo.toml8
-rw-r--r--compiler/stable_mir/README.md112
-rw-r--r--compiler/stable_mir/rust-toolchain.toml3
-rw-r--r--compiler/stable_mir/src/fold.rs245
-rw-r--r--compiler/stable_mir/src/lib.rs242
-rw-r--r--compiler/stable_mir/src/mir.rs3
-rw-r--r--compiler/stable_mir/src/mir/body.rs447
-rw-r--r--compiler/stable_mir/src/ty.rs567
-rw-r--r--compiler/stable_mir/src/visitor.rs203
9 files changed, 1830 insertions, 0 deletions
diff --git a/compiler/stable_mir/Cargo.toml b/compiler/stable_mir/Cargo.toml
new file mode 100644
index 000000000..c61e217bf
--- /dev/null
+++ b/compiler/stable_mir/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "stable_mir"
+version = "0.1.0-preview"
+edition = "2021"
+
+[dependencies]
+tracing = "0.1"
+scoped-tls = "1.0"
diff --git a/compiler/stable_mir/README.md b/compiler/stable_mir/README.md
new file mode 100644
index 000000000..31dee955f
--- /dev/null
+++ b/compiler/stable_mir/README.md
@@ -0,0 +1,112 @@
+This crate is regularly synced with its mirror in the rustc repo at `compiler/rustc_smir`.
+
+We use `git subtree` for this to preserve commits and allow the rustc repo to
+edit these crates without having to touch this repo. This keeps the crates compiling
+while allowing us to independently work on them here. The effort of keeping them in
+sync is pushed entirely onto us, without affecting rustc workflows negatively.
+This may change in the future, but changes to policy should only be done via a
+compiler team MCP.
+
+## Instructions for working on this crate locally
+
+Since the crate is the same in the rustc repo and here, the dependencies on rustc_* crates
+will only either work here or there, but never in both places at the same time. Thus we use
+optional dependencies on the rustc_* crates, requiring local development to use
+
+```
+cargo build --no-default-features -Zavoid-dev-deps
+```
+
+in order to compile successfully.
+
+## Instructions for syncing
+
+### Updating this repository
+
+In the rustc repo, execute
+
+```
+git subtree push --prefix=compiler/rustc_smir url_to_your_fork_of_project_stable_mir some_feature_branch
+```
+
+and then open a PR of your `some_feature_branch` against https://github.com/rust-lang/project-stable-mir
+
+### Updating the rustc library
+
+First we need to bump our stack limit, as the rustc repo otherwise quickly hits that:
+
+```
+ulimit -s 60000
+```
+
+#### Maximum function recursion depth (1000) reached
+
+Then we need to disable `dash` as the default shell for sh scripts, as otherwise we run into a
+hard limit of a recursion depth of 1000:
+
+```
+sudo dpkg-reconfigure dash
+```
+
+and then select `No` to disable dash.
+
+
+#### Patching your `git worktree`
+
+The regular git worktree does not scale to repos of the size of the rustc repo.
+So download the `git-subtree.sh` from https://github.com/gitgitgadget/git/pull/493/files and run
+
+```
+sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree
+sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
+sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
+```
+
+#### Actually doing a sync
+
+In the rustc repo, execute
+
+```
+git subtree pull --prefix=compiler/rustc_smir https://github.com/rust-lang/project-stable-mir smir
+```
+
+Note: only ever sync to rustc from the project-stable-mir's `smir` branch. Do not sync with your own forks.
+
+Then open a PR against rustc just like a regular PR.
+
+## Stable MIR Design
+
+The stable-mir will follow a similar approach to proc-macro2. It’s
+implementation will eventually be broken down into two main crates:
+
+- `stable_mir`: Public crate, to be published on crates.io, which will contain
+the stable data structure as well as proxy APIs to make calls to the
+compiler.
+- `rustc_smir`: The compiler crate that will translate from internal MIR to
+SMIR. This crate will also implement APIs that will be invoked by
+stable-mir to query the compiler for more information.
+
+This will help tools to communicate with the rust compiler via stable APIs. Tools will depend on
+`stable_mir` crate, which will invoke the compiler using APIs defined in `rustc_smir`. I.e.:
+
+```
+ ┌──────────────────────────────────┐ ┌──────────────────────────────────┐
+ │ External Tool ┌──────────┐ │ │ ┌──────────┐ Rust Compiler │
+ │ │ │ │ │ │ │ │
+ │ │stable_mir| │ │ │rustc_smir│ │
+ │ │ │ ├──────────►| │ │ │
+ │ │ │ │◄──────────┤ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ └──────────┘ │ │ └──────────┘ │
+ └──────────────────────────────────┘ └──────────────────────────────────┘
+```
+
+More details can be found here:
+https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view
+
+For now, the code for these two crates are in separate modules of this crate.
+The modules have the same name for simplicity. We also have a third module,
+`rustc_internal` which will expose APIs and definitions that allow users to
+gather information from internal MIR constructs that haven't been exposed in
+the `stable_mir` module.
diff --git a/compiler/stable_mir/rust-toolchain.toml b/compiler/stable_mir/rust-toolchain.toml
new file mode 100644
index 000000000..d75e8e33b
--- /dev/null
+++ b/compiler/stable_mir/rust-toolchain.toml
@@ -0,0 +1,3 @@
+[toolchain]
+channel = "nightly-2023-06-14"
+components = [ "rustfmt", "rustc-dev" ]
diff --git a/compiler/stable_mir/src/fold.rs b/compiler/stable_mir/src/fold.rs
new file mode 100644
index 000000000..6471b2c2a
--- /dev/null
+++ b/compiler/stable_mir/src/fold.rs
@@ -0,0 +1,245 @@
+use std::ops::ControlFlow;
+
+use crate::Opaque;
+
+use super::ty::{
+ Allocation, Binder, Const, ConstDef, ConstantKind, ExistentialPredicate, FnSig, GenericArgKind,
+ GenericArgs, Promoted, Region, RigidTy, TermKind, Ty, TyKind, UnevaluatedConst,
+};
+
+pub trait Folder: Sized {
+ type Break;
+ fn fold_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break, Ty> {
+ ty.super_fold(self)
+ }
+ fn fold_const(&mut self, c: &Const) -> ControlFlow<Self::Break, Const> {
+ c.super_fold(self)
+ }
+ fn fold_reg(&mut self, reg: &Region) -> ControlFlow<Self::Break, Region> {
+ reg.super_fold(self)
+ }
+}
+
+pub trait Foldable: Sized + Clone {
+ fn fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ self.super_fold(folder)
+ }
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self>;
+}
+
+impl Foldable for Ty {
+ fn fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ folder.fold_ty(self)
+ }
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ let mut kind = self.kind();
+ match &mut kind {
+ super::ty::TyKind::RigidTy(ty) => *ty = ty.fold(folder)?,
+ super::ty::TyKind::Alias(_, alias) => alias.args = alias.args.fold(folder)?,
+ super::ty::TyKind::Param(_) => {}
+ super::ty::TyKind::Bound(_, _) => {}
+ }
+ ControlFlow::Continue(kind.into())
+ }
+}
+
+impl Foldable for Const {
+ fn fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ folder.fold_const(self)
+ }
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ let mut this = self.clone();
+ match &mut this.literal {
+ super::ty::ConstantKind::Allocated(alloc) => *alloc = alloc.fold(folder)?,
+ super::ty::ConstantKind::Unevaluated(uv) => *uv = uv.fold(folder)?,
+ super::ty::ConstantKind::Param(_) => {}
+ }
+ this.ty = this.ty.fold(folder)?;
+ ControlFlow::Continue(this)
+ }
+}
+
+impl Foldable for Opaque {
+ fn super_fold<V: Folder>(&self, _folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(self.clone())
+ }
+}
+
+impl Foldable for Allocation {
+ fn super_fold<V: Folder>(&self, _folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(self.clone())
+ }
+}
+
+impl Foldable for UnevaluatedConst {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ let UnevaluatedConst { def, args, promoted } = self;
+ ControlFlow::Continue(UnevaluatedConst {
+ def: def.fold(folder)?,
+ args: args.fold(folder)?,
+ promoted: promoted.fold(folder)?,
+ })
+ }
+}
+
+impl Foldable for ConstDef {
+ fn super_fold<V: Folder>(&self, _folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(*self)
+ }
+}
+
+impl<T: Foldable> Foldable for Option<T> {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(match self {
+ Some(val) => Some(val.fold(folder)?),
+ None => None,
+ })
+ }
+}
+
+impl Foldable for Promoted {
+ fn super_fold<V: Folder>(&self, _folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(*self)
+ }
+}
+
+impl Foldable for GenericArgs {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(GenericArgs(self.0.fold(folder)?))
+ }
+}
+
+impl Foldable for Region {
+ fn fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ folder.fold_reg(self)
+ }
+ fn super_fold<V: Folder>(&self, _: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(self.clone())
+ }
+}
+
+impl Foldable for GenericArgKind {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ let mut this = self.clone();
+ match &mut this {
+ GenericArgKind::Lifetime(lt) => *lt = lt.fold(folder)?,
+ GenericArgKind::Type(t) => *t = t.fold(folder)?,
+ GenericArgKind::Const(c) => *c = c.fold(folder)?,
+ }
+ ControlFlow::Continue(this)
+ }
+}
+
+impl Foldable for RigidTy {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ let mut this = self.clone();
+ match &mut this {
+ RigidTy::Bool
+ | RigidTy::Char
+ | RigidTy::Int(_)
+ | RigidTy::Uint(_)
+ | RigidTy::Float(_)
+ | RigidTy::Never
+ | RigidTy::Foreign(_)
+ | RigidTy::Str => {}
+ RigidTy::Array(t, c) => {
+ *t = t.fold(folder)?;
+ *c = c.fold(folder)?;
+ }
+ RigidTy::Slice(inner) => *inner = inner.fold(folder)?,
+ RigidTy::RawPtr(ty, _) => *ty = ty.fold(folder)?,
+ RigidTy::Ref(reg, ty, _) => {
+ *reg = reg.fold(folder)?;
+ *ty = ty.fold(folder)?
+ }
+ RigidTy::FnDef(_, args) => *args = args.fold(folder)?,
+ RigidTy::FnPtr(sig) => *sig = sig.fold(folder)?,
+ RigidTy::Closure(_, args) => *args = args.fold(folder)?,
+ RigidTy::Generator(_, args, _) => *args = args.fold(folder)?,
+ RigidTy::Dynamic(pred, r, _) => {
+ *pred = pred.fold(folder)?;
+ *r = r.fold(folder)?;
+ }
+ RigidTy::Tuple(fields) => *fields = fields.fold(folder)?,
+ RigidTy::Adt(_, args) => *args = args.fold(folder)?,
+ }
+ ControlFlow::Continue(this)
+ }
+}
+
+impl<T: Foldable> Foldable for Vec<T> {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ let mut this = self.clone();
+ for arg in &mut this {
+ *arg = arg.fold(folder)?;
+ }
+ ControlFlow::Continue(this)
+ }
+}
+
+impl<T: Foldable> Foldable for Binder<T> {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(Self {
+ value: self.value.fold(folder)?,
+ bound_vars: self.bound_vars.clone(),
+ })
+ }
+}
+
+impl Foldable for ExistentialPredicate {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ let mut this = self.clone();
+ match &mut this {
+ ExistentialPredicate::Trait(tr) => tr.generic_args = tr.generic_args.fold(folder)?,
+ ExistentialPredicate::Projection(p) => {
+ p.term = p.term.fold(folder)?;
+ p.generic_args = p.generic_args.fold(folder)?;
+ }
+ ExistentialPredicate::AutoTrait(_) => {}
+ }
+ ControlFlow::Continue(this)
+ }
+}
+
+impl Foldable for TermKind {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(match self {
+ TermKind::Type(t) => TermKind::Type(t.fold(folder)?),
+ TermKind::Const(c) => TermKind::Const(c.fold(folder)?),
+ })
+ }
+}
+
+impl Foldable for FnSig {
+ fn super_fold<V: Folder>(&self, folder: &mut V) -> ControlFlow<V::Break, Self> {
+ ControlFlow::Continue(Self {
+ inputs_and_output: self.inputs_and_output.fold(folder)?,
+ c_variadic: self.c_variadic,
+ unsafety: self.unsafety,
+ abi: self.abi.clone(),
+ })
+ }
+}
+
+pub enum Never {}
+
+/// In order to instantiate a `Foldable`'s generic parameters with specific arguments,
+/// `GenericArgs` can be used as a `Folder` that replaces all mentions of generic params
+/// with the entries in its list.
+impl Folder for GenericArgs {
+ type Break = Never;
+
+ fn fold_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break, Ty> {
+ ControlFlow::Continue(match ty.kind() {
+ TyKind::Param(p) => self[p],
+ _ => *ty,
+ })
+ }
+
+ fn fold_const(&mut self, c: &Const) -> ControlFlow<Self::Break, Const> {
+ ControlFlow::Continue(match &c.literal {
+ ConstantKind::Param(p) => self[p.clone()].clone(),
+ _ => c.clone(),
+ })
+ }
+}
diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs
new file mode 100644
index 000000000..104985493
--- /dev/null
+++ b/compiler/stable_mir/src/lib.rs
@@ -0,0 +1,242 @@
+//! The WIP stable interface to rustc internals.
+//!
+//! For more information see <https://github.com/rust-lang/project-stable-mir>
+//!
+//! # 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::cell::Cell;
+use std::fmt;
+use std::fmt::Debug;
+
+use self::ty::{
+ GenericPredicates, Generics, ImplDef, ImplTrait, Span, TraitDecl, TraitDef, Ty, TyKind,
+};
+
+#[macro_use]
+extern crate scoped_tls;
+
+pub mod fold;
+pub mod mir;
+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;
+
+/// A unique identification number for each item accessible for the current compilation unit.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct DefId(pub usize);
+
+impl Debug for DefId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("DefId")
+ .field("id", &self.0)
+ .field("name", &with(|cx| cx.name_of_def_id(*self)))
+ .finish()
+ }
+}
+
+/// A unique identification number for each provenance
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct AllocId(pub usize);
+
+/// A list of crate items.
+pub type CrateItems = Vec<CrateItem>;
+
+/// A list of trait decls.
+pub type TraitDecls = Vec<TraitDef>;
+
+/// A list of impl trait decls.
+pub type ImplTraitDecls = Vec<ImplDef>;
+
+/// An error type used to represent an error that has already been reported by the compiler.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum CompilerError<T> {
+ /// Internal compiler error (I.e.: Compiler crashed).
+ ICE,
+ /// Compilation failed.
+ CompilationFailed,
+ /// Compilation was interrupted.
+ Interrupted(T),
+ /// Compilation skipped. This happens when users invoke rustc to retrieve information such as
+ /// --version.
+ Skipped,
+}
+
+/// Holds information about a crate.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct Crate {
+ pub id: CrateNum,
+ pub name: Symbol,
+ pub is_local: bool,
+}
+
+pub type DefKind = Opaque;
+
+/// Holds information about an item in the crate.
+/// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to
+/// use this item.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct CrateItem(pub DefId);
+
+impl CrateItem {
+ pub fn body(&self) -> mir::Body {
+ with(|cx| cx.mir_body(self.0))
+ }
+
+ pub fn span(&self) -> Span {
+ with(|cx| cx.span_of_an_item(self.0))
+ }
+
+ pub fn name(&self) -> String {
+ with(|cx| cx.name_of_def_id(self.0))
+ }
+
+ pub fn kind(&self) -> DefKind {
+ with(|cx| cx.def_kind(self.0))
+ }
+}
+
+/// 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<CrateItem> {
+ 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 with the given name.
+pub fn find_crate(name: &str) -> Option<Crate> {
+ with(|cx| cx.find_crate(name))
+}
+
+/// Try to find a crate with the given name.
+pub fn external_crates() -> Vec<Crate> {
+ 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 trait_decl(trait_def: &TraitDef) -> TraitDecl {
+ with(|cx| cx.trait_decl(trait_def))
+}
+
+pub fn all_trait_impls() -> ImplTraitDecls {
+ with(|cx| cx.all_trait_impls())
+}
+
+pub fn trait_impl(trait_impl: &ImplDef) -> ImplTrait {
+ with(|cx| cx.trait_impl(trait_impl))
+}
+
+pub trait Context {
+ fn entry_fn(&mut self) -> Option<CrateItem>;
+ /// Retrieve all items of the local crate that have a MIR associated with them.
+ fn all_local_items(&mut self) -> CrateItems;
+ fn mir_body(&mut self, item: DefId) -> mir::Body;
+ fn all_trait_decls(&mut self) -> TraitDecls;
+ fn trait_decl(&mut self, trait_def: &TraitDef) -> TraitDecl;
+ fn all_trait_impls(&mut self) -> ImplTraitDecls;
+ fn trait_impl(&mut self, trait_impl: &ImplDef) -> ImplTrait;
+ fn generics_of(&mut self, def_id: DefId) -> Generics;
+ fn predicates_of(&mut self, def_id: DefId) -> GenericPredicates;
+ fn explicit_predicates_of(&mut 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<Crate>;
+
+ /// Find a crate with the given name.
+ fn find_crate(&self, name: &str) -> Option<Crate>;
+
+ /// Prints the name of given `DefId`
+ fn name_of_def_id(&self, def_id: DefId) -> String;
+
+ /// Prints a human readable form of `Span`
+ fn print_span(&self, span: Span) -> String;
+
+ /// Prints the kind of given `DefId`
+ fn def_kind(&mut self, def_id: DefId) -> DefKind;
+
+ /// `Span` of an item
+ fn span_of_an_item(&mut self, def_id: DefId) -> Span;
+
+ /// Obtain the representation of a type.
+ fn ty_kind(&mut self, ty: Ty) -> TyKind;
+
+ /// Create a new `Ty` from scratch without information from rustc.
+ fn mk_ty(&mut self, kind: TyKind) -> Ty;
+}
+
+// A thread local variable that stores a pointer to the tables mapping between TyCtxt
+// datastructures and stable MIR datastructures
+scoped_thread_local! (static TLV: Cell<*mut ()>);
+
+pub fn run(mut context: impl Context, f: impl FnOnce()) {
+ assert!(!TLV.is_set());
+ fn g<'a>(mut context: &mut (dyn Context + 'a), f: impl FnOnce()) {
+ let ptr: *mut () = &mut context as *mut &mut _ as _;
+ TLV.set(&Cell::new(ptr), || {
+ f();
+ });
+ }
+ g(&mut context, f);
+}
+
+/// Loads the current context and calls a function with it.
+/// Do not nest these, as that will ICE.
+pub fn with<R>(f: impl FnOnce(&mut dyn Context) -> R) -> R {
+ assert!(TLV.is_set());
+ TLV.with(|tlv| {
+ let ptr = tlv.get();
+ assert!(!ptr.is_null());
+ f(unsafe { *(ptr as *mut &mut dyn Context) })
+ })
+}
+
+/// A type that provides internal information but that can still be used for debug purpose.
+#[derive(Clone)]
+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<T: Debug>(value: &T) -> Opaque {
+ Opaque(format!("{value:?}"))
+}
diff --git a/compiler/stable_mir/src/mir.rs b/compiler/stable_mir/src/mir.rs
new file mode 100644
index 000000000..a9dbc3463
--- /dev/null
+++ b/compiler/stable_mir/src/mir.rs
@@ -0,0 +1,3 @@
+mod body;
+
+pub use body::*;
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
new file mode 100644
index 000000000..f93a1a3a9
--- /dev/null
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -0,0 +1,447 @@
+use crate::ty::{AdtDef, ClosureDef, Const, GeneratorDef, GenericArgs, Movability, Region};
+use crate::Opaque;
+use crate::{ty::Ty, Span};
+
+#[derive(Clone, Debug)]
+pub struct Body {
+ pub blocks: Vec<BasicBlock>,
+ pub locals: Vec<Ty>,
+}
+
+#[derive(Clone, Debug)]
+pub struct BasicBlock {
+ pub statements: Vec<Statement>,
+ pub terminator: Terminator,
+}
+
+#[derive(Clone, Debug)]
+pub enum Terminator {
+ Goto {
+ target: usize,
+ },
+ SwitchInt {
+ discr: Operand,
+ targets: Vec<SwitchTarget>,
+ otherwise: usize,
+ },
+ Resume,
+ Abort,
+ Return,
+ Unreachable,
+ Drop {
+ place: Place,
+ target: usize,
+ unwind: UnwindAction,
+ },
+ Call {
+ func: Operand,
+ args: Vec<Operand>,
+ destination: Place,
+ target: Option<usize>,
+ unwind: UnwindAction,
+ },
+ Assert {
+ cond: Operand,
+ expected: bool,
+ msg: AssertMessage,
+ target: usize,
+ unwind: UnwindAction,
+ },
+ GeneratorDrop,
+ InlineAsm {
+ template: String,
+ operands: Vec<InlineAsmOperand>,
+ options: String,
+ line_spans: String,
+ destination: Option<usize>,
+ unwind: UnwindAction,
+ },
+}
+
+#[derive(Clone, Debug)]
+pub struct InlineAsmOperand {
+ pub in_value: Option<Operand>,
+ pub out_place: Option<Place>,
+ // 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(Clone, Debug)]
+pub enum UnwindAction {
+ Continue,
+ Unreachable,
+ Terminate,
+ Cleanup(usize),
+}
+
+#[derive(Clone, Debug)]
+pub enum AssertMessage {
+ BoundsCheck { len: Operand, index: Operand },
+ Overflow(BinOp, Operand, Operand),
+ OverflowNeg(Operand),
+ DivisionByZero(Operand),
+ RemainderByZero(Operand),
+ ResumedAfterReturn(GeneratorKind),
+ ResumedAfterPanic(GeneratorKind),
+ MisalignedPointerDereference { required: Operand, found: Operand },
+}
+
+#[derive(Clone, Debug)]
+pub enum BinOp {
+ Add,
+ AddUnchecked,
+ Sub,
+ SubUnchecked,
+ Mul,
+ MulUnchecked,
+ Div,
+ Rem,
+ BitXor,
+ BitAnd,
+ BitOr,
+ Shl,
+ ShlUnchecked,
+ Shr,
+ ShrUnchecked,
+ Eq,
+ Lt,
+ Le,
+ Ne,
+ Ge,
+ Gt,
+ Offset,
+}
+
+#[derive(Clone, Debug)]
+pub enum UnOp {
+ Not,
+ Neg,
+}
+
+#[derive(Clone, Debug)]
+pub enum GeneratorKind {
+ Async(AsyncGeneratorKind),
+ Gen,
+}
+
+#[derive(Clone, Debug)]
+pub enum AsyncGeneratorKind {
+ Block,
+ Closure,
+ Fn,
+}
+
+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)]
+pub enum FakeReadCause {
+ ForMatchGuard,
+ ForMatchedPlace(LocalDefId),
+ ForGuardBinding,
+ ForLet(LocalDefId),
+ ForIndex,
+}
+
+/// Describes what kind of retag is to be performed
+#[derive(Clone, Debug)]
+pub enum RetagKind {
+ FnEntry,
+ TwoPhase,
+ Raw,
+ Default,
+}
+
+#[derive(Clone, Debug)]
+pub enum Variance {
+ Covariant,
+ Invariant,
+ Contravariant,
+ Bivariant,
+}
+
+#[derive(Clone, Debug)]
+pub struct CopyNonOverlapping {
+ pub src: Operand,
+ pub dst: Operand,
+ pub count: Operand,
+}
+
+#[derive(Clone, Debug)]
+pub enum NonDivergingIntrinsic {
+ Assume(Operand),
+ CopyNonOverlapping(CopyNonOverlapping),
+}
+
+#[derive(Clone, Debug)]
+pub enum Statement {
+ 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)]
+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(Mutability, 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 `Generator`. After
+ /// generator lowering, `Generator` aggregate kinds are disallowed too.
+ Aggregate(AggregateKind, Vec<Operand>),
+
+ /// * `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, Const),
+
+ /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
+ ///
+ /// 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),
+}
+
+#[derive(Clone, Debug)]
+pub enum AggregateKind {
+ Array(Ty),
+ Tuple,
+ Adt(AdtDef, VariantIdx, GenericArgs, Option<UserTypeAnnotationIndex>, Option<FieldIdx>),
+ Closure(ClosureDef, GenericArgs),
+ Generator(GeneratorDef, GenericArgs, Movability),
+}
+
+#[derive(Clone, Debug)]
+pub enum Operand {
+ Copy(Place),
+ Move(Place),
+ Constant(Constant),
+}
+
+#[derive(Clone, Debug)]
+pub struct Place {
+ pub local: Local,
+ pub projection: String,
+}
+
+#[derive(Clone, Debug)]
+pub struct UserTypeProjection {
+ pub base: UserTypeAnnotationIndex,
+ pub projection: String,
+}
+
+pub type Local = usize;
+
+type FieldIdx = usize;
+
+/// The source-order index of a variant in a type.
+pub type VariantIdx = usize;
+
+type UserTypeAnnotationIndex = usize;
+
+#[derive(Clone, Debug)]
+pub struct Constant {
+ pub span: Span,
+ pub user_ty: Option<UserTypeAnnotationIndex>,
+ pub literal: Const,
+}
+
+#[derive(Clone, Debug)]
+pub struct SwitchTarget {
+ pub value: u128,
+ pub target: usize,
+}
+
+#[derive(Clone, Debug)]
+pub enum BorrowKind {
+ /// Data must be immutable and is aliasable.
+ Shared,
+
+ /// 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`.
+ Fake,
+
+ /// Data is mutable and not aliasable.
+ Mut {
+ /// `true` if this borrow arose from method-call auto-ref
+ kind: MutBorrowKind,
+ },
+}
+
+#[derive(Clone, Debug)]
+pub enum MutBorrowKind {
+ Default,
+ TwoPhaseBorrow,
+ ClosureCapture,
+}
+
+#[derive(Clone, Debug)]
+pub enum Mutability {
+ Not,
+ Mut,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum Safety {
+ Unsafe,
+ Normal,
+}
+
+#[derive(Clone, Debug)]
+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 an 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 fat pointer.
+ /// This will do things like convert thin pointers to fat
+ /// pointers, or convert structs containing thin pointers to
+ /// structs containing fat pointers, or convert between fat
+ /// pointers.
+ Unsize,
+}
+
+#[derive(Clone, Debug)]
+pub enum CastKind {
+ PointerExposeAddress,
+ PointerFromExposedAddress,
+ PointerCoercion(PointerCoercion),
+ DynStar,
+ IntToInt,
+ FloatToInt,
+ FloatToFloat,
+ IntToFloat,
+ PtrToPtr,
+ FnPtrToPtr,
+ Transmute,
+}
+
+#[derive(Clone, Debug)]
+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<FieldIdx>),
+}
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
new file mode 100644
index 000000000..6029e3c11
--- /dev/null
+++ b/compiler/stable_mir/src/ty.rs
@@ -0,0 +1,567 @@
+use super::{
+ mir::Safety,
+ mir::{Body, Mutability},
+ with, AllocId, DefId, Symbol,
+};
+use crate::Opaque;
+use std::fmt::{self, Debug, Formatter};
+
+#[derive(Copy, Clone)]
+pub struct Ty(pub 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()
+ }
+}
+
+impl Ty {
+ pub fn kind(&self) -> TyKind {
+ with(|context| context.ty_kind(*self))
+ }
+}
+
+impl From<TyKind> for Ty {
+ fn from(value: TyKind) -> Self {
+ with(|context| context.mk_ty(value))
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Const {
+ pub literal: ConstantKind,
+ pub ty: Ty,
+}
+
+type Ident = Opaque;
+
+#[derive(Debug, Clone)]
+pub struct Region {
+ pub kind: RegionKind,
+}
+
+#[derive(Debug, Clone)]
+pub enum RegionKind {
+ ReEarlyBound(EarlyBoundRegion),
+ ReLateBound(DebruijnIndex, BoundRegion),
+ ReStatic,
+ RePlaceholder(Placeholder<BoundRegion>),
+ ReErased,
+}
+
+pub(crate) type DebruijnIndex = u32;
+
+#[derive(Debug, Clone)]
+pub struct EarlyBoundRegion {
+ pub def_id: RegionDef,
+ pub index: u32,
+ pub name: Symbol,
+}
+
+pub(crate) type BoundVar = u32;
+
+#[derive(Debug, Clone)]
+pub struct BoundRegion {
+ pub var: BoundVar,
+ pub kind: BoundRegionKind,
+}
+
+pub(crate) type UniverseIndex = u32;
+
+#[derive(Debug, Clone)]
+pub struct Placeholder<T> {
+ pub universe: UniverseIndex,
+ pub bound: T,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct Span(pub 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.print_span(*self)))
+ .finish()
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum TyKind {
+ RigidTy(RigidTy),
+ Alias(AliasKind, AliasTy),
+ Param(ParamTy),
+ Bound(usize, BoundTy),
+}
+
+#[derive(Clone, Debug)]
+pub enum RigidTy {
+ Bool,
+ Char,
+ Int(IntTy),
+ Uint(UintTy),
+ Float(FloatTy),
+ Adt(AdtDef, GenericArgs),
+ Foreign(ForeignDef),
+ Str,
+ Array(Ty, Const),
+ Slice(Ty),
+ RawPtr(Ty, Mutability),
+ Ref(Region, Ty, Mutability),
+ FnDef(FnDef, GenericArgs),
+ FnPtr(PolyFnSig),
+ Closure(ClosureDef, GenericArgs),
+ Generator(GeneratorDef, GenericArgs, Movability),
+ Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind),
+ Never,
+ Tuple(Vec<Ty>),
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum IntTy {
+ Isize,
+ I8,
+ I16,
+ I32,
+ I64,
+ I128,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum UintTy {
+ Usize,
+ U8,
+ U16,
+ U32,
+ U64,
+ U128,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum FloatTy {
+ F32,
+ F64,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum Movability {
+ Static,
+ Movable,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct ForeignDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct FnDef(pub DefId);
+
+impl FnDef {
+ pub fn body(&self) -> Body {
+ with(|ctx| ctx.mir_body(self.0))
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct ClosureDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct GeneratorDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct ParamDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct BrNamedDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct AdtDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct AliasDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct TraitDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct GenericDef(pub DefId);
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct ConstDef(pub DefId);
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct ImplDef(pub DefId);
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct RegionDef(pub DefId);
+
+#[derive(Clone, Debug)]
+pub struct GenericArgs(pub Vec<GenericArgKind>);
+
+impl std::ops::Index<ParamTy> for GenericArgs {
+ type Output = Ty;
+
+ fn index(&self, index: ParamTy) -> &Self::Output {
+ self.0[index.index as usize].expect_ty()
+ }
+}
+
+impl std::ops::Index<ParamConst> for GenericArgs {
+ type Output = Const;
+
+ fn index(&self, index: ParamConst) -> &Self::Output {
+ self.0[index.index as usize].expect_const()
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum GenericArgKind {
+ Lifetime(Region),
+ Type(Ty),
+ Const(Const),
+}
+
+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) -> &Const {
+ match self {
+ GenericArgKind::Const(c) => c,
+ _ => panic!("{self:?}"),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum TermKind {
+ Type(Ty),
+ Const(Const),
+}
+
+#[derive(Clone, Debug)]
+pub enum AliasKind {
+ Projection,
+ Inherent,
+ Opaque,
+ Weak,
+}
+
+#[derive(Clone, Debug)]
+pub struct AliasTy {
+ pub def_id: AliasDef,
+ pub args: GenericArgs,
+}
+
+pub type PolyFnSig = Binder<FnSig>;
+
+#[derive(Clone, Debug)]
+pub struct FnSig {
+ pub inputs_and_output: Vec<Ty>,
+ pub c_variadic: bool,
+ pub unsafety: Safety,
+ pub abi: Abi,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+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,
+ AmdGpuKernel,
+ EfiApi,
+ AvrInterrupt,
+ AvrNonBlockingInterrupt,
+ CCmseNonSecureCall,
+ Wasm,
+ System { unwind: bool },
+ RustIntrinsic,
+ RustCall,
+ PlatformIntrinsic,
+ Unadjusted,
+ RustCold,
+ RiscvInterruptM,
+ RiscvInterruptS,
+}
+
+#[derive(Clone, Debug)]
+pub struct Binder<T> {
+ pub value: T,
+ pub bound_vars: Vec<BoundVariableKind>,
+}
+
+#[derive(Clone, Debug)]
+pub struct EarlyBinder<T> {
+ pub value: T,
+}
+
+#[derive(Clone, Debug)]
+pub enum BoundVariableKind {
+ Ty(BoundTyKind),
+ Region(BoundRegionKind),
+ Const,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum BoundTyKind {
+ Anon,
+ Param(ParamDef, String),
+}
+
+#[derive(Clone, Debug)]
+pub enum BoundRegionKind {
+ BrAnon,
+ BrNamed(BrNamedDef, String),
+ BrEnv,
+}
+
+#[derive(Clone, Debug)]
+pub enum DynKind {
+ Dyn,
+ DynStar,
+}
+
+#[derive(Clone, Debug)]
+pub enum ExistentialPredicate {
+ Trait(ExistentialTraitRef),
+ Projection(ExistentialProjection),
+ AutoTrait(TraitDef),
+}
+
+#[derive(Clone, Debug)]
+pub struct ExistentialTraitRef {
+ pub def_id: TraitDef,
+ pub generic_args: GenericArgs,
+}
+
+#[derive(Clone, Debug)]
+pub struct ExistentialProjection {
+ pub def_id: TraitDef,
+ pub generic_args: GenericArgs,
+ pub term: TermKind,
+}
+
+#[derive(Clone, Debug)]
+pub struct ParamTy {
+ pub index: u32,
+ pub name: String,
+}
+
+#[derive(Clone, Debug)]
+pub struct BoundTy {
+ pub var: usize,
+ pub kind: BoundTyKind,
+}
+
+pub type Bytes = Vec<Option<u8>>;
+pub type Size = usize;
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub struct Prov(pub AllocId);
+pub type Align = u64;
+pub type Promoted = u32;
+pub type InitMaskMaterialized = Vec<u64>;
+
+/// Stores the provenance information of pointers stored in memory.
+#[derive(Clone, Debug)]
+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)]
+pub struct Allocation {
+ pub bytes: Bytes,
+ pub provenance: ProvenanceMap,
+ pub align: Align,
+ pub mutability: Mutability,
+}
+
+#[derive(Clone, Debug)]
+pub enum ConstantKind {
+ Allocated(Allocation),
+ Unevaluated(UnevaluatedConst),
+ Param(ParamConst),
+}
+
+#[derive(Clone, Debug)]
+pub struct ParamConst {
+ pub index: u32,
+ pub name: String,
+}
+
+#[derive(Clone, Debug)]
+pub struct UnevaluatedConst {
+ pub def: ConstDef,
+ pub args: GenericArgs,
+ pub promoted: Option<Promoted>,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum TraitSpecializationKind {
+ None,
+ Marker,
+ AlwaysApplicable,
+}
+
+#[derive(Clone, Debug)]
+pub struct TraitDecl {
+ pub def_id: TraitDef,
+ pub unsafety: 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 specialization_kind: TraitSpecializationKind,
+ pub must_implement_one_of: Option<Vec<Ident>>,
+ 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<TraitRef>;
+
+#[derive(Clone, Debug)]
+pub struct TraitRef {
+ pub def_id: TraitDef,
+ pub args: GenericArgs,
+}
+
+#[derive(Clone, Debug)]
+pub struct Generics {
+ pub parent: Option<GenericDef>,
+ pub parent_count: usize,
+ pub params: Vec<GenericParamDef>,
+ pub param_def_id_to_index: Vec<(GenericDef, u32)>,
+ pub has_self: bool,
+ pub has_late_bound_regions: Option<Span>,
+ pub host_effect_index: Option<usize>,
+}
+
+#[derive(Clone, Debug)]
+pub enum GenericParamDefKind {
+ Lifetime,
+ Type { has_default: bool, synthetic: bool },
+ Const { has_default: bool },
+}
+
+#[derive(Clone, Debug)]
+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<TraitDef>,
+ pub predicates: Vec<(PredicateKind, Span)>,
+}
+
+#[derive(Clone, Debug)]
+pub enum PredicateKind {
+ Clause(ClauseKind),
+ ObjectSafe(TraitDef),
+ ClosureKind(ClosureDef, GenericArgs, ClosureKind),
+ SubType(SubtypePredicate),
+ Coerce(CoercePredicate),
+ ConstEquate(Const, Const),
+ Ambiguous,
+ AliasRelate(TermKind, TermKind, AliasRelationDirection),
+}
+
+#[derive(Clone, Debug)]
+pub enum ClauseKind {
+ Trait(TraitPredicate),
+ RegionOutlives(RegionOutlivesPredicate),
+ TypeOutlives(TypeOutlivesPredicate),
+ Projection(ProjectionPredicate),
+ ConstArgHasType(Const, Ty),
+ WellFormed(GenericArgKind),
+ ConstEvaluatable(Const),
+}
+
+#[derive(Clone, Debug)]
+pub enum ClosureKind {
+ Fn,
+ FnMut,
+ FnOnce,
+}
+
+#[derive(Clone, Debug)]
+pub struct SubtypePredicate {
+ pub a: Ty,
+ pub b: Ty,
+}
+
+#[derive(Clone, Debug)]
+pub struct CoercePredicate {
+ pub a: Ty,
+ pub b: Ty,
+}
+
+#[derive(Clone, Debug)]
+pub enum AliasRelationDirection {
+ Equate,
+ Subtype,
+}
+
+#[derive(Clone, Debug)]
+pub struct TraitPredicate {
+ pub trait_ref: TraitRef,
+ pub polarity: ImplPolarity,
+}
+
+#[derive(Clone, Debug)]
+pub struct OutlivesPredicate<A, B>(pub A, pub B);
+
+pub type RegionOutlivesPredicate = OutlivesPredicate<Region, Region>;
+pub type TypeOutlivesPredicate = OutlivesPredicate<Ty, Region>;
+
+#[derive(Clone, Debug)]
+pub struct ProjectionPredicate {
+ pub projection_ty: AliasTy,
+ pub term: TermKind,
+}
+
+#[derive(Clone, Debug)]
+pub enum ImplPolarity {
+ Positive,
+ Negative,
+ Reservation,
+}
diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs
new file mode 100644
index 000000000..961009581
--- /dev/null
+++ b/compiler/stable_mir/src/visitor.rs
@@ -0,0 +1,203 @@
+use std::ops::ControlFlow;
+
+use crate::Opaque;
+
+use super::ty::{
+ Allocation, Binder, Const, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs,
+ Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst,
+};
+
+pub trait Visitor: Sized {
+ type Break;
+ fn visit_ty(&mut self, ty: &Ty) -> ControlFlow<Self::Break> {
+ ty.super_visit(self)
+ }
+ fn visit_const(&mut self, c: &Const) -> ControlFlow<Self::Break> {
+ c.super_visit(self)
+ }
+ fn visit_reg(&mut self, reg: &Region) -> ControlFlow<Self::Break> {
+ reg.super_visit(self)
+ }
+}
+
+pub trait Visitable {
+ fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ self.super_visit(visitor)
+ }
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break>;
+}
+
+impl Visitable for Ty {
+ fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ visitor.visit_ty(self)
+ }
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ 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 Const {
+ fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ visitor.visit_const(self)
+ }
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ match &self.literal {
+ super::ty::ConstantKind::Allocated(alloc) => alloc.visit(visitor)?,
+ super::ty::ConstantKind::Unevaluated(uv) => uv.visit(visitor)?,
+ super::ty::ConstantKind::Param(_) => {}
+ }
+ self.ty.visit(visitor)
+ }
+}
+
+impl Visitable for Opaque {
+ fn super_visit<V: Visitor>(&self, _visitor: &mut V) -> ControlFlow<V::Break> {
+ ControlFlow::Continue(())
+ }
+}
+
+impl Visitable for Allocation {
+ fn super_visit<V: Visitor>(&self, _visitor: &mut V) -> ControlFlow<V::Break> {
+ ControlFlow::Continue(())
+ }
+}
+
+impl Visitable for UnevaluatedConst {
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ let UnevaluatedConst { def, args, promoted } = self;
+ def.visit(visitor)?;
+ args.visit(visitor)?;
+ promoted.visit(visitor)
+ }
+}
+
+impl Visitable for ConstDef {
+ fn super_visit<V: Visitor>(&self, _visitor: &mut V) -> ControlFlow<V::Break> {
+ ControlFlow::Continue(())
+ }
+}
+
+impl<T: Visitable> Visitable for Option<T> {
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ match self {
+ Some(val) => val.visit(visitor),
+ None => ControlFlow::Continue(()),
+ }
+ }
+}
+
+impl Visitable for Promoted {
+ fn super_visit<V: Visitor>(&self, _visitor: &mut V) -> ControlFlow<V::Break> {
+ ControlFlow::Continue(())
+ }
+}
+
+impl Visitable for GenericArgs {
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ self.0.visit(visitor)
+ }
+}
+
+impl Visitable for Region {
+ fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ visitor.visit_reg(self)
+ }
+
+ fn super_visit<V: Visitor>(&self, _: &mut V) -> ControlFlow<V::Break> {
+ ControlFlow::Continue(())
+ }
+}
+
+impl Visitable for GenericArgKind {
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ 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<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ 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::Slice(inner) => inner.visit(visitor),
+ RigidTy::RawPtr(ty, _) => ty.visit(visitor),
+ RigidTy::Ref(reg, ty, _) => {
+ reg.visit(visitor);
+ ty.visit(visitor)
+ }
+ RigidTy::FnDef(_, args) => args.visit(visitor),
+ RigidTy::FnPtr(sig) => sig.visit(visitor),
+ RigidTy::Closure(_, args) => args.visit(visitor),
+ RigidTy::Generator(_, args, _) => args.visit(visitor),
+ RigidTy::Dynamic(pred, r, _) => {
+ pred.visit(visitor)?;
+ r.visit(visitor)
+ }
+ RigidTy::Tuple(fields) => fields.visit(visitor),
+ RigidTy::Adt(_, args) => args.visit(visitor),
+ }
+ }
+}
+
+impl<T: Visitable> Visitable for Vec<T> {
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ for arg in self {
+ arg.visit(visitor)?;
+ }
+ ControlFlow::Continue(())
+ }
+}
+
+impl<T: Visitable> Visitable for Binder<T> {
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ self.value.visit(visitor)
+ }
+}
+
+impl Visitable for ExistentialPredicate {
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ 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<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ match self {
+ TermKind::Type(t) => t.visit(visitor),
+ TermKind::Const(c) => c.visit(visitor),
+ }
+ }
+}
+
+impl Visitable for FnSig {
+ fn super_visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> {
+ self.inputs_and_output.visit(visitor)
+ }
+}