summaryrefslogtreecommitdiffstats
path: root/compiler/stable_mir
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
commit9918693037dce8aa4bb6f08741b6812923486c18 (patch)
tree21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /compiler/stable_mir
parentReleasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff)
downloadrustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz
rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/stable_mir')
-rw-r--r--compiler/stable_mir/src/abi.rs283
-rw-r--r--compiler/stable_mir/src/compiler_interface.rs218
-rw-r--r--compiler/stable_mir/src/crate_def.rs69
-rw-r--r--compiler/stable_mir/src/error.rs20
-rw-r--r--compiler/stable_mir/src/lib.rs186
-rw-r--r--compiler/stable_mir/src/mir.rs2
-rw-r--r--compiler/stable_mir/src/mir/alloc.rs83
-rw-r--r--compiler/stable_mir/src/mir/body.rs622
-rw-r--r--compiler/stable_mir/src/mir/mono.rs206
-rw-r--r--compiler/stable_mir/src/mir/pretty.rs483
-rw-r--r--compiler/stable_mir/src/mir/visit.rs78
-rw-r--r--compiler/stable_mir/src/target.rs50
-rw-r--r--compiler/stable_mir/src/ty.rs761
-rw-r--r--compiler/stable_mir/src/visitor.rs1
14 files changed, 2811 insertions, 251 deletions
diff --git a/compiler/stable_mir/src/abi.rs b/compiler/stable_mir/src/abi.rs
new file mode 100644
index 000000000..53dac6abe
--- /dev/null
+++ b/compiler/stable_mir/src/abi.rs
@@ -0,0 +1,283 @@
+use crate::compiler_interface::with;
+use crate::mir::FieldIdx;
+use crate::ty::{Align, IndexedVal, Size, Ty, VariantIdx};
+use crate::Opaque;
+use std::num::NonZeroUsize;
+use std::ops::RangeInclusive;
+
+/// A function ABI definition.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct FnAbi {
+ /// The types of each argument.
+ pub args: Vec<ArgAbi>,
+
+ /// 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)]
+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)]
+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)]
+pub struct TyAndLayout {
+ pub ty: Ty,
+ pub layout: Layout,
+}
+
+/// The layout of a type in memory.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct LayoutShape {
+ /// The fields location withing 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 == 0 && self.abi_align == 1
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+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)]
+pub enum FieldsShape {
+ /// Scalar primitives and `!`, which never have fields.
+ Primitive,
+
+ /// All fields start at no offset. The `usize` is the field count.
+ Union(NonZeroUsize),
+
+ /// 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<Size>,
+ },
+}
+
+impl FieldsShape {
+ pub fn fields_by_offset_order(&self) -> Vec<FieldIdx> {
+ match self {
+ FieldsShape::Primitive => vec![],
+ FieldsShape::Union(_) | FieldsShape::Array { .. } => (0..self.count()).collect(),
+ FieldsShape::Arbitrary { offsets, .. } => {
+ let mut indices = (0..offsets.len()).collect::<Vec<_>>();
+ 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)]
+pub enum VariantsShape {
+ /// 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<LayoutShape>,
+ },
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+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<VariantIdx>,
+ 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)]
+pub enum ValueAbi {
+ Uninhabited,
+ 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::Uninhabited
+ | ValueAbi::Scalar(_)
+ | ValueAbi::ScalarPair(..)
+ | ValueAbi::Vector { .. } => false,
+ ValueAbi::Aggregate { sized } => !sized,
+ }
+ }
+}
+
+/// We currently do not support `Scalar`, and use opaque instead.
+type Scalar = Opaque;
+
+/// General language calling conventions.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum CallConvention {
+ C,
+ Rust,
+
+ Cold,
+ PreserveMost,
+ PreserveAll,
+
+ // Target-specific calling conventions.
+ ArmAapcs,
+ CCmseNonSecureCall,
+
+ Msp430Intr,
+
+ PtxKernel,
+
+ X86Fastcall,
+ X86Intr,
+ X86Stdcall,
+ X86ThisCall,
+ X86VectorCall,
+
+ X86_64SysV,
+ X86_64Win64,
+
+ AmdGpuKernel,
+ AvrInterrupt,
+ AvrNonBlockingInterrupt,
+
+ RiscvInterrupt,
+}
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs
new file mode 100644
index 000000000..f52e50605
--- /dev/null
+++ b/compiler/stable_mir/src/compiler_interface.rs
@@ -0,0 +1,218 @@
+//! 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::mir::alloc::{AllocId, GlobalAlloc};
+use crate::mir::mono::{Instance, InstanceDef, StaticDef};
+use crate::mir::Body;
+use crate::target::MachineInfo;
+use crate::ty::{
+ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, GenericArgs,
+ GenericPredicates, Generics, ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl,
+ TraitDef, Ty, TyKind, VariantDef,
+};
+use crate::{
+ mir, Crate, CrateItem, CrateItems, DefId, Error, Filename, ImplTraitDecls, ItemKind, Symbol,
+ TraitDecls,
+};
+
+/// 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<CrateItem>;
+ /// 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 all_trait_decls(&self) -> TraitDecls;
+ fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl;
+ fn all_trait_impls(&self) -> 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<Crate>;
+
+ /// Find a crate with the given name.
+ fn find_crates(&self, name: &str) -> Vec<Crate>;
+
+ /// Returns the name of given `DefId`
+ fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol;
+
+ /// 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 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 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<FieldDef>;
+
+ /// Evaluate constant as a target usize.
+ fn eval_target_usize(&self, cnst: &Const) -> Result<u64, Error>;
+
+ /// Create a target usize constant for the given value.
+ fn usize_to_const(&self, val: u64) -> Result<Const, Error>;
+
+ /// Create a new type from the given kind.
+ fn new_rigid_ty(&self, kind: RigidTy) -> Ty;
+
+ /// Create a new box type, `Box<T>`, 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 const_literal(&self, cnst: &Const) -> String;
+
+ /// `Span` of an item
+ fn span_of_an_item(&self, def_id: DefId) -> Span;
+
+ /// 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<Body>;
+
+ /// Get the instance type with generic substitutions 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;
+
+ /// 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<Instance>;
+
+ /// 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<Instance>;
+
+ /// Resolve instance for a closure with the requested type.
+ fn resolve_closure(
+ &self,
+ def: ClosureDef,
+ args: &GenericArgs,
+ kind: ClosureKind,
+ ) -> Option<Instance>;
+
+ /// Evaluate a static's initializer.
+ fn eval_static_initializer(&self, def: StaticDef) -> Result<Allocation, Error>;
+
+ /// Try to evaluate an instance into a constant.
+ fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result<Allocation, Error>;
+
+ /// 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<AllocId>;
+ 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<FnAbi, Error>;
+
+ /// Get the layout of a type.
+ fn ty_layout(&self, ty: Ty) -> Result<Layout, Error>;
+
+ /// Get the layout shape.
+ fn layout_shape(&self, id: Layout) -> LayoutShape;
+}
+
+// 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<*const ()>);
+
+pub fn run<F, T>(context: &dyn Context, f: F) -> Result<T, Error>
+where
+ F: FnOnce() -> T,
+{
+ if TLV.is_set() {
+ Err(Error::from("StableMIR already running"))
+ } else {
+ let ptr: *const () = &context as *const &_ 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<R>(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
new file mode 100644
index 000000000..70ca9e682
--- /dev/null
+++ b/compiler/stable_mir/src/crate_def.rs
@@ -0,0 +1,69 @@
+//! 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 crate::ty::Span;
+use crate::{with, Crate, Symbol};
+
+/// A unique identification number for each item accessible for the current compilation unit.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct DefId(pub(crate) usize);
+
+/// 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.
+ fn name(&self) -> Symbol {
+ let def_id = self.def_id();
+ with(|cx| cx.def_name(def_id, 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.
+ fn trimmed_name(&self) -> Symbol {
+ let def_id = self.def_id();
+ with(|cx| cx.def_name(def_id, true))
+ }
+
+ /// 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))
+ }
+}
+
+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
+ }
+ }
+ };
+}
diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs
index 199106914..7085fa937 100644
--- a/compiler/stable_mir/src/error.rs
+++ b/compiler/stable_mir/src/error.rs
@@ -4,9 +4,13 @@
//! - [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::convert::From;
use std::fmt::{Debug, Display, Formatter};
-use std::{error, fmt};
+use std::{error, fmt, io};
+
+macro_rules! error {
+ ($fmt: literal $(,)?) => { Error(format!($fmt)) };
+ ($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) };
+ }
/// An error type used to represent an error that has already been reported by the compiler.
#[derive(Clone, Copy, PartialEq, Eq)]
@@ -23,11 +27,11 @@ pub enum CompilerError<T> {
}
/// A generic error to represent an API request that cannot be fulfilled.
-#[derive(Debug)]
-pub struct Error(String);
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Error(pub(crate) String);
impl Error {
- pub(crate) fn new(msg: String) -> Self {
+ pub fn new(msg: String) -> Self {
Self(msg)
}
}
@@ -74,3 +78,9 @@ where
impl error::Error for Error {}
impl<T> error::Error for CompilerError<T> where T: Display + Debug {}
+
+impl From<io::Error> 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 f316671b2..9194f1e6b 100644
--- a/compiler/stable_mir/src/lib.rs
+++ b/compiler/stable_mir/src/lib.rs
@@ -16,45 +16,45 @@
//!
//! The goal is to eventually be published on
//! [crates.io](https://crates.io).
+#![feature(type_alias_impl_trait)]
+#[macro_use]
+extern crate scoped_tls;
-use crate::mir::mono::InstanceDef;
-use crate::mir::Body;
-use std::cell::Cell;
use std::fmt;
use std::fmt::Debug;
+use std::io;
-use self::ty::{
- GenericPredicates, Generics, ImplDef, ImplTrait, IndexedVal, LineInfo, Span, TraitDecl,
- TraitDef, Ty, TyKind,
-};
+use crate::compiler_interface::with;
+pub use crate::crate_def::CrateDef;
+pub use crate::crate_def::DefId;
+pub use crate::error::*;
+use crate::mir::pretty::function_name;
+use crate::mir::Body;
+use crate::mir::Mutability;
+use crate::ty::{ImplDef, ImplTrait, IndexedVal, Span, TraitDecl, TraitDef, Ty};
+pub mod abi;
+#[macro_use]
+pub mod crate_def;
+pub mod compiler_interface;
#[macro_use]
-extern crate scoped_tls;
-
pub mod error;
pub mod mir;
+pub mod target;
pub mod ty;
pub mod visitor;
-pub use error::*;
-use mir::mono::Instance;
-use ty::{FnDef, GenericArgs};
-
/// 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(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)))
+ .field("name", &with(|cx| cx.def_name(*self, false)))
.finish()
}
}
@@ -69,19 +69,6 @@ impl IndexedVal for DefId {
}
}
-/// A unique identification number for each provenance
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct AllocId(usize);
-
-impl IndexedVal for AllocId {
- fn to_val(index: usize) -> Self {
- AllocId(index)
- }
- fn to_index(&self) -> usize {
- self.0
- }
-}
-
/// A list of crate items.
pub type CrateItems = Vec<CrateItem>;
@@ -99,12 +86,26 @@ pub struct Crate {
pub is_local: bool,
}
-pub type DefKind = Opaque;
-pub type Filename = Opaque;
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+pub enum ItemKind {
+ Fn,
+ Static,
+ Const,
+ Ctor(CtorKind),
+}
-/// Holds information about an item in the crate.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct CrateItem(pub DefId);
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+pub enum CtorKind {
+ Const,
+ Fn,
+}
+
+pub type Filename = String;
+
+crate_def! {
+ /// Holds information about an item in a crate.
+ pub CrateItem;
+}
impl CrateItem {
pub fn body(&self) -> mir::Body {
@@ -115,17 +116,26 @@ impl CrateItem {
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))
+ 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))
+ }
+
+ pub fn dump<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
+ writeln!(w, "{}", function_name(*self))?;
+ self.body().dump(w)
+ }
}
/// Return the function where execution starts if the current
@@ -171,96 +181,8 @@ pub fn trait_impl(trait_impl: &ImplDef) -> ImplTrait {
with(|cx| cx.trait_impl(trait_impl))
}
-pub trait Context {
- fn entry_fn(&self) -> Option<CrateItem>;
- /// Retrieve all items of the local crate that have a MIR associated with them.
- fn all_local_items(&self) -> CrateItems;
- fn mir_body(&self, item: DefId) -> mir::Body;
- fn all_trait_decls(&self) -> TraitDecls;
- fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl;
- fn all_trait_impls(&self) -> 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<Crate>;
-
- /// Find a crate with the given name.
- fn find_crates(&self, name: &str) -> Vec<Crate>;
-
- /// Returns the name of given `DefId`
- fn name_of_def_id(&self, def_id: DefId) -> String;
-
- /// 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 def_kind(&self, def_id: DefId) -> DefKind;
-
- /// `Span` of an item
- fn span_of_an_item(&self, def_id: DefId) -> Span;
-
- /// Obtain the representation of a type.
- fn ty_kind(&self, ty: Ty) -> TyKind;
-
- /// Get the body of an Instance.
- /// FIXME: Monomorphize the body.
- fn instance_body(&self, instance: InstanceDef) -> Body;
-
- /// Get the instance type with generic substitutions applied and lifetimes erased.
- fn instance_ty(&self, instance: InstanceDef) -> Ty;
-
- /// Get the instance.
- fn instance_def_id(&self, instance: InstanceDef) -> DefId;
-
- /// Get the instance mangled name.
- fn instance_mangled_name(&self, instance: InstanceDef) -> String;
-
- /// Convert a non-generic crate item into an instance.
- /// This function will panic if the item is generic.
- fn mono_instance(&self, item: CrateItem) -> 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<Instance>;
-}
-
-// 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<*const ()>);
-
-pub fn run(context: &dyn Context, f: impl FnOnce()) {
- assert!(!TLV.is_set());
- let ptr: *const () = &context as *const &_ as _;
- TLV.set(&Cell::new(ptr), || {
- 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(&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) })
- })
-}
-
/// A type that provides internal information but that can still be used for debug purpose.
-#[derive(Clone, Eq, PartialEq)]
+#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Opaque(String);
impl std::fmt::Display for Opaque {
@@ -271,7 +193,7 @@ impl std::fmt::Display for Opaque {
impl std::fmt::Debug for Opaque {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{:?}", self.0)
+ write!(f, "{}", self.0)
}
}
diff --git a/compiler/stable_mir/src/mir.rs b/compiler/stable_mir/src/mir.rs
index 2e1714b49..82555461d 100644
--- a/compiler/stable_mir/src/mir.rs
+++ b/compiler/stable_mir/src/mir.rs
@@ -1,5 +1,7 @@
+pub mod alloc;
mod body;
pub mod mono;
+pub mod pretty;
pub mod visit;
pub use body::*;
diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs
new file mode 100644
index 000000000..c780042ff
--- /dev/null
+++ b/compiler/stable_mir/src/mir/alloc.rs
@@ -0,0 +1,83 @@
+//! This module provides methods to retrieve allocation information, such as static variables.
+use crate::mir::mono::{Instance, StaticDef};
+use crate::target::{Endian, MachineInfo};
+use crate::ty::{Allocation, Binder, ExistentialTraitRef, IndexedVal, Ty};
+use crate::{with, Error};
+use std::io::Read;
+
+/// 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)]
+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<Binder<ExistentialTraitRef>>),
+ /// 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<AllocId> 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<AllocId> {
+ with(|cx| cx.vtable_allocation(self))
+ }
+}
+
+/// A unique identification number for each provenance
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+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<u128, Error> {
+ let mut buf = [0u8; std::mem::size_of::<u128>()];
+ match MachineInfo::target_endianess() {
+ Endian::Little => {
+ bytes.read(&mut buf)?;
+ Ok(u128::from_le_bytes(buf))
+ }
+ Endian::Big => {
+ bytes.read(&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<i128, Error> {
+ let mut buf = [0u8; std::mem::size_of::<i128>()];
+ match MachineInfo::target_endianess() {
+ Endian::Little => {
+ bytes.read(&mut buf)?;
+ Ok(i128::from_le_bytes(buf))
+ }
+ Endian::Big => {
+ bytes.read(&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
index 069337836..b8fd9370a 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -1,36 +1,59 @@
-use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, Ty};
-use crate::Opaque;
-use crate::Span;
-
+use crate::mir::pretty::{function_body, pretty_statement, pretty_terminator};
+use crate::ty::{
+ AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind,
+ VariantIdx,
+};
+use crate::{Error, Opaque, Span, Symbol};
+use std::io;
/// The SMIR representation of a single function.
#[derive(Clone, Debug)]
pub struct Body {
pub blocks: Vec<BasicBlock>,
- // 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.
+ /// 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.
+ /// 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<VarDebugInfo>,
+
+ /// 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<Local>,
+
+ /// 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<BasicBlock>, locals: LocalDecls, arg_count: usize) -> Self {
+ pub fn new(
+ blocks: Vec<BasicBlock>,
+ locals: LocalDecls,
+ arg_count: usize,
+ var_debug_info: Vec<VarDebugInfo>,
+ spread_arg: Option<Local>,
+ 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 }
+ Self { blocks, locals, arg_count, var_debug_info, spread_arg, span }
}
/// Return local that holds this function's return value.
@@ -56,6 +79,44 @@ impl Body {
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<Item = (Local, &LocalDecl)> {
+ self.locals.iter().enumerate()
+ }
+
+ pub fn dump<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
+ writeln!(w, "{}", function_body(self))?;
+ self.blocks
+ .iter()
+ .enumerate()
+ .map(|(index, block)| -> io::Result<()> {
+ writeln!(w, " bb{}: {{", index)?;
+ let _ = block
+ .statements
+ .iter()
+ .map(|statement| -> io::Result<()> {
+ writeln!(w, "{}", pretty_statement(&statement.kind))?;
+ Ok(())
+ })
+ .collect::<Vec<_>>();
+ pretty_terminator(&block.terminator.kind, w)?;
+ writeln!(w, "").unwrap();
+ writeln!(w, " }}").unwrap();
+ Ok(())
+ })
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(())
+ }
+
+ pub fn spread_arg(&self) -> Option<Local> {
+ self.spread_arg
+ }
}
type LocalDecls = Vec<LocalDecl>;
@@ -64,9 +125,10 @@ type LocalDecls = Vec<LocalDecl>;
pub struct LocalDecl {
pub ty: Ty,
pub span: Span,
+ pub mutability: Mutability,
}
-#[derive(Clone, Debug)]
+#[derive(Clone, PartialEq, Eq, Debug)]
pub struct BasicBlock {
pub statements: Vec<Statement>,
pub terminator: Terminator,
@@ -78,15 +140,22 @@ pub struct Terminator {
pub span: Span,
}
+impl Terminator {
+ pub fn successors(&self) -> Successors {
+ self.kind.successors()
+ }
+}
+
+pub type Successors = Vec<BasicBlockIdx>;
+
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TerminatorKind {
Goto {
- target: usize,
+ target: BasicBlockIdx,
},
SwitchInt {
discr: Operand,
- targets: Vec<SwitchTarget>,
- otherwise: usize,
+ targets: SwitchTargets,
},
Resume,
Abort,
@@ -94,34 +163,81 @@ pub enum TerminatorKind {
Unreachable,
Drop {
place: Place,
- target: usize,
+ target: BasicBlockIdx,
unwind: UnwindAction,
},
Call {
func: Operand,
args: Vec<Operand>,
destination: Place,
- target: Option<usize>,
+ target: Option<BasicBlockIdx>,
unwind: UnwindAction,
},
Assert {
cond: Operand,
expected: bool,
msg: AssertMessage,
- target: usize,
+ target: BasicBlockIdx,
unwind: UnwindAction,
},
- CoroutineDrop,
InlineAsm {
template: String,
operands: Vec<InlineAsmOperand>,
options: String,
line_spans: String,
- destination: Option<usize>,
+ destination: Option<BasicBlockIdx>,
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)]
pub struct InlineAsmOperand {
pub in_value: Option<Operand>,
@@ -131,12 +247,12 @@ pub struct InlineAsmOperand {
pub raw_rpr: String,
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum UnwindAction {
Continue,
Unreachable,
Terminate,
- Cleanup(usize),
+ Cleanup(BasicBlockIdx),
}
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -151,7 +267,58 @@ pub enum AssertMessage {
MisalignedPointerDereference { required: Operand, found: Operand },
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+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::Async(_)) => {
+ Ok("`async fn` resumed after completion")
+ }
+ AssertMessage::ResumedAfterReturn(CoroutineKind::Gen(_)) => {
+ Ok("`async gen fn` resumed after completion")
+ }
+ AssertMessage::ResumedAfterReturn(CoroutineKind::AsyncGen(_)) => {
+ Ok("`gen fn` should just keep returning `AssertMessage::None` after completion")
+ }
+ AssertMessage::ResumedAfterPanic(CoroutineKind::Coroutine) => {
+ Ok("coroutine resumed after panicking")
+ }
+ AssertMessage::ResumedAfterPanic(CoroutineKind::Async(_)) => {
+ Ok("`async fn` resumed after panicking")
+ }
+ AssertMessage::ResumedAfterPanic(CoroutineKind::Gen(_)) => {
+ Ok("`async gen fn` resumed after panicking")
+ }
+ AssertMessage::ResumedAfterPanic(CoroutineKind::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")
+ }
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BinOp {
Add,
AddUnchecked,
@@ -177,7 +344,47 @@ pub enum BinOp {
Offset,
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+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 {
+ match self {
+ BinOp::Add
+ | BinOp::AddUnchecked
+ | BinOp::Sub
+ | BinOp::SubUnchecked
+ | BinOp::Mul
+ | BinOp::MulUnchecked
+ | BinOp::Div
+ | BinOp::Rem
+ | BinOp::BitXor
+ | BinOp::BitAnd
+ | BinOp::BitOr => {
+ assert_eq!(lhs_ty, rhs_ty);
+ assert!(lhs_ty.kind().is_primitive());
+ lhs_ty
+ }
+ BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => {
+ assert!(lhs_ty.kind().is_primitive());
+ assert!(rhs_ty.kind().is_primitive());
+ lhs_ty
+ }
+ BinOp::Offset => {
+ assert!(lhs_ty.kind().is_raw_ptr());
+ assert!(rhs_ty.kind().is_integral());
+ lhs_ty
+ }
+ BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
+ assert_eq!(lhs_ty, rhs_ty);
+ let lhs_kind = lhs_ty.kind();
+ assert!(lhs_kind.is_primitive() || lhs_kind.is_raw_ptr() || lhs_kind.is_fn_ptr());
+ Ty::bool_ty()
+ }
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum UnOp {
Not,
Neg,
@@ -188,9 +395,10 @@ pub enum CoroutineKind {
Async(CoroutineSource),
Coroutine,
Gen(CoroutineSource),
+ AsyncGen(CoroutineSource),
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CoroutineSource {
Block,
Closure,
@@ -214,7 +422,7 @@ pub enum FakeReadCause {
}
/// Describes what kind of retag is to be performed
-#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum RetagKind {
FnEntry,
TwoPhase,
@@ -222,7 +430,7 @@ pub enum RetagKind {
Default,
}
-#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Variance {
Covariant,
Invariant,
@@ -378,6 +586,63 @@ pub enum Rvalue {
Use(Operand),
}
+impl Rvalue {
+ pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> {
+ 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))
+ }
+ 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(UnOp::Not | UnOp::Neg, operand) => operand.ty(locals),
+ 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::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::<Result<Vec<_>, _>>()?,
+ )),
+ 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))
+ }
+ },
+ Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)),
+ Rvalue::CopyForDeref(place) => place.ty(locals),
+ }
+ }
+}
+
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AggregateKind {
Array(Ty),
@@ -398,23 +663,188 @@ pub enum Operand {
pub struct Place {
pub local: Local,
/// projection out of a place (access a field, deref a pointer, etc)
- pub projection: String,
+ pub projection: Vec<ProjectionElem>,
+}
+
+impl From<Local> for Place {
+ fn from(local: Local) -> Self {
+ Place { local, projection: vec![] }
+ }
+}
+
+/// Debug information pertaining to a user variable.
+#[derive(Clone, Debug, Eq, PartialEq)]
+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<VarDebugInfoFragment>,
+
+ /// 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<u16>,
+}
+
+impl VarDebugInfo {
+ /// Return a local variable if this info is related to one.
+ pub fn local(&self) -> Option<Local> {
+ 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)]
+pub struct SourceInfo {
+ pub span: Span,
+ pub scope: SourceScope,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct VarDebugInfoFragment {
+ pub ty: Ty,
+ pub projection: Vec<ProjectionElem>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum VarDebugInfoContents {
+ Place(Place),
+ Const(ConstOperand),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct ConstOperand {
+ pub span: Span,
+ pub user_ty: Option<UserTypeAnnotationIndex>,
+ pub const_: Const,
+}
+
+// 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<Local, Ty>) 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)]
+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. 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)]
pub struct UserTypeProjection {
pub base: UserTypeAnnotationIndex,
- pub projection: String,
+
+ pub projection: Opaque,
}
pub type Local = usize;
pub const RETURN_LOCAL: Local = 0;
-type FieldIdx = usize;
-
-/// The source-order index of a variant in a type.
-pub type VariantIdx = usize;
+/// 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;
@@ -425,13 +855,45 @@ pub struct Constant {
pub literal: Const,
}
+/// The possible branch sites of a [TerminatorKind::SwitchInt].
#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct SwitchTarget {
- pub value: u128,
- pub target: usize,
+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,
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+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<Item = (u128, BasicBlockIdx)> + '_ {
+ 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)]
pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
@@ -449,14 +911,25 @@ pub enum BorrowKind {
},
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+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)]
pub enum MutBorrowKind {
Default,
TwoPhaseBorrow,
ClosureCapture,
}
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Mutability {
Not,
Mut,
@@ -468,7 +941,7 @@ pub enum Safety {
Normal,
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum PointerCoercion {
/// Go from a fn-item type to a fn-pointer type.
ReifyFnPointer,
@@ -495,7 +968,7 @@ pub enum PointerCoercion {
Unsize,
}
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CastKind {
PointerExposeAddress,
PointerFromExposedAddress,
@@ -521,10 +994,16 @@ pub enum NullOp {
}
impl Operand {
- pub fn ty(&self, locals: &[LocalDecl]) -> Ty {
+ /// 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<Ty, Error> {
match self {
Operand::Copy(place) | Operand::Move(place) => place.ty(locals),
- Operand::Constant(c) => c.ty(),
+ Operand::Constant(c) => Ok(c.ty()),
}
}
}
@@ -536,8 +1015,57 @@ impl Constant {
}
impl Place {
- pub fn ty(&self, locals: &[LocalDecl]) -> Ty {
- let _start_ty = locals[self.local].ty;
- todo!("Implement projection")
+ /// 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<Ty, Error> {
+ let start_ty = locals[self.local].ty;
+ self.projection.iter().fold(Ok(start_ty), |place_ty, elem| {
+ let ty = place_ty?;
+ match elem {
+ 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, Error> {
+ 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<Ty, Error> {
+ 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<Ty, Error> {
+ 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
index 8f5333498..6c791ae85 100644
--- a/compiler/stable_mir/src/mir/mono.rs
+++ b/compiler/stable_mir/src/mir/mono.rs
@@ -1,16 +1,18 @@
+use crate::abi::FnAbi;
+use crate::crate_def::CrateDef;
use crate::mir::Body;
-use crate::ty::{FnDef, GenericArgs, IndexedVal, Ty};
-use crate::{with, CrateItem, DefId, Error, Opaque};
-use std::fmt::Debug;
+use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty};
+use crate::{with, CrateItem, DefId, Error, ItemKind, Opaque, Symbol};
+use std::fmt::{Debug, Formatter};
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum MonoItem {
Fn(Instance),
Static(StaticDef),
GlobalAsm(Opaque),
}
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Instance {
/// The type of instance.
pub kind: InstanceKind,
@@ -19,33 +21,75 @@ pub struct Instance {
pub def: InstanceDef,
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum InstanceKind {
/// A user defined item.
Item,
/// A compiler intrinsic function.
Intrinsic,
/// A virtual function definition stored in a VTable.
- Virtual,
+ /// 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.
- pub fn body(&self) -> Body {
+ pub fn body(&self) -> Option<Body> {
with(|context| context.instance_body(self.def))
}
+ /// Check whether this instance has a body available.
+ ///
+ /// 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 substitutions applied and lifetimes erased.
pub fn ty(&self) -> Ty {
with(|context| context.instance_ty(self.def))
}
- pub fn mangled_name(&self) -> String {
+ /// Retrieve information about this instance binary interface.
+ pub fn fn_abi(&self) -> Result<FnAbi, Error> {
+ 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<u8>::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))
+ }
+
/// Resolve an instance starting from a function definition and generic arguments.
pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> {
with(|context| {
@@ -54,6 +98,64 @@ impl Instance {
})
})
}
+
+ /// 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<Instance, crate::Error> {
+ 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<Instance, crate::Error> {
+ 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))
+ }
+
+ /// 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<Allocation, Error> {
+ with(|cx| cx.eval_instance(self.def, const_ty))
+ }
+}
+
+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.
@@ -63,8 +165,9 @@ impl TryFrom<CrateItem> for Instance {
fn try_from(item: CrateItem) -> Result<Self, Self::Error> {
with(|context| {
- if !context.requires_monomorphization(item.0) {
- Ok(context.mono_instance(item))
+ 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()))
}
@@ -78,19 +181,86 @@ impl TryFrom<Instance> for CrateItem {
type Error = crate::Error;
fn try_from(value: Instance) -> Result<Self, Self::Error> {
- if value.kind == InstanceKind::Item {
- Ok(CrateItem(with(|context| context.instance_def_id(value.def))))
+ 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<Instance> for MonoItem {
+ fn from(value: Instance) -> Self {
+ MonoItem::Fn(value)
+ }
+}
+
+impl From<StaticDef> for MonoItem {
+ fn from(value: StaticDef) -> Self {
+ MonoItem::Static(value)
+ }
+}
+
+impl From<StaticDef> for CrateItem {
+ fn from(value: StaticDef) -> Self {
+ CrateItem(value.0)
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+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.
+ pub StaticDef;
+}
+
+impl TryFrom<CrateItem> for StaticDef {
+ type Error = crate::Error;
+
+ fn try_from(value: CrateItem) -> Result<Self, Self::Error> {
+ if matches!(value.kind(), ItemKind::Static) {
+ Ok(StaticDef(value.0))
} else {
- Err(Error::new(format!("Item kind `{:?}` cannot be converted", value.kind)))
+ Err(Error::new(format!("Expected a static item, but found: {value:?}")))
}
}
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub struct InstanceDef(usize);
+impl TryFrom<Instance> for StaticDef {
+ type Error = crate::Error;
+
+ fn try_from(value: Instance) -> Result<Self, Self::Error> {
+ StaticDef::try_from(CrateItem::try_from(value)?)
+ }
+}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct StaticDef(pub DefId);
+impl From<StaticDef> 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<Allocation, Error> {
+ with(|cx| cx.eval_static_initializer(*self))
+ }
+}
impl IndexedVal for InstanceDef {
fn to_val(index: usize) -> Self {
diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs
new file mode 100644
index 000000000..8b7b488d3
--- /dev/null
+++ b/compiler/stable_mir/src/mir/pretty.rs
@@ -0,0 +1,483 @@
+use crate::crate_def::CrateDef;
+use crate::mir::{Operand, Rvalue, StatementKind, UnwindAction};
+use crate::ty::{DynKind, FloatTy, IntTy, RigidTy, TyKind, UintTy};
+use crate::{with, Body, CrateItem, Mutability};
+use std::io::Write;
+use std::{io, iter};
+
+use super::{AssertMessage, BinOp, TerminatorKind};
+
+pub fn function_name(item: CrateItem) -> String {
+ let mut pretty_name = String::new();
+ let body = item.body();
+ pretty_name.push_str("fn ");
+ pretty_name.push_str(item.name().as_str());
+ if body.arg_locals().is_empty() {
+ pretty_name.push_str("()");
+ } else {
+ pretty_name.push_str("(");
+ }
+ body.arg_locals().iter().enumerate().for_each(|(index, local)| {
+ pretty_name.push_str(format!("_{}: ", index).as_str());
+ pretty_name.push_str(&pretty_ty(local.ty.kind()));
+ });
+ if !body.arg_locals().is_empty() {
+ pretty_name.push_str(")");
+ }
+ let return_local = body.ret_local();
+ pretty_name.push_str(" -> ");
+ pretty_name.push_str(&pretty_ty(return_local.ty.kind()));
+ pretty_name.push_str(" {");
+ pretty_name
+}
+
+pub fn function_body(body: &Body) -> String {
+ let mut pretty_body = String::new();
+ body.inner_locals().iter().enumerate().for_each(|(index, local)| {
+ pretty_body.push_str(" ");
+ pretty_body.push_str(format!("let {}", ret_mutability(&local.mutability)).as_str());
+ pretty_body.push_str(format!("_{}: ", index).as_str());
+ pretty_body.push_str(format!("{}", pretty_ty(local.ty.kind())).as_str());
+ pretty_body.push_str(";\n");
+ });
+ pretty_body.push_str("}");
+ pretty_body
+}
+
+pub fn ret_mutability(mutability: &Mutability) -> String {
+ match mutability {
+ Mutability::Not => "".to_string(),
+ Mutability::Mut => "mut ".to_string(),
+ }
+}
+
+pub fn pretty_statement(statement: &StatementKind) -> String {
+ let mut pretty = String::new();
+ match statement {
+ StatementKind::Assign(place, rval) => {
+ pretty.push_str(format!(" _{} = ", place.local).as_str());
+ pretty.push_str(format!("{}", &pretty_rvalue(rval)).as_str());
+ }
+ // FIXME: Add rest of the statements
+ StatementKind::FakeRead(_, _) => {
+ return String::from("StatementKind::FakeRead:Unimplemented");
+ }
+ StatementKind::SetDiscriminant { .. } => {
+ return String::from("StatementKind::SetDiscriminant:Unimplemented");
+ }
+ StatementKind::Deinit(_) => return String::from("StatementKind::Deinit:Unimplemented"),
+ StatementKind::StorageLive(_) => {
+ return String::from("StatementKind::StorageLive:Unimplemented");
+ }
+ StatementKind::StorageDead(_) => {
+ return String::from("StatementKind::StorageDead:Unimplemented");
+ }
+ StatementKind::Retag(_, _) => return String::from("StatementKind::Retag:Unimplemented"),
+ StatementKind::PlaceMention(_) => {
+ return String::from("StatementKind::PlaceMention:Unimplemented");
+ }
+ StatementKind::AscribeUserType { .. } => {
+ return String::from("StatementKind::AscribeUserType:Unimplemented");
+ }
+ StatementKind::Coverage(_) => return String::from("StatementKind::Coverage:Unimplemented"),
+ StatementKind::Intrinsic(_) => {
+ return String::from("StatementKind::Intrinsic:Unimplemented");
+ }
+ StatementKind::ConstEvalCounter => {
+ return String::from("StatementKind::ConstEvalCounter:Unimplemented");
+ }
+ StatementKind::Nop => return String::from("StatementKind::Nop:Unimplemented"),
+ }
+ pretty
+}
+
+pub fn pretty_terminator<W: io::Write>(terminator: &TerminatorKind, w: &mut W) -> io::Result<()> {
+ write!(w, "{}", pretty_terminator_head(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 = |fmt: &mut dyn Write| -> io::Result<()> {
+ write!(fmt, "unwind ")?;
+ match terminator.unwind() {
+ None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
+ Some(UnwindAction::Continue) => write!(fmt, "continue"),
+ Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
+ Some(UnwindAction::Terminate) => write!(fmt, "terminate"),
+ }
+ };
+
+ match (successor_count, show_unwind) {
+ (0, false) => Ok(()),
+ (0, true) => {
+ write!(w, " -> ")?;
+ fmt_unwind(w)?;
+ Ok(())
+ }
+ (1, false) => {
+ write!(w, " -> {:?}", successors[0])?;
+ Ok(())
+ }
+ _ => {
+ write!(w, " -> [")?;
+ for (i, target) in successors.iter().enumerate() {
+ if i > 0 {
+ write!(w, ", ")?;
+ }
+ write!(w, "{}: bb{:?}", labels[i], target)?;
+ }
+ if show_unwind {
+ write!(w, ", ")?;
+ fmt_unwind(w)?;
+ }
+ write!(w, "]")
+ }
+ }?;
+
+ Ok(())
+}
+
+pub fn pretty_terminator_head(terminator: &TerminatorKind) -> String {
+ use self::TerminatorKind::*;
+ let mut pretty = String::new();
+ match terminator {
+ Goto { .. } => format!(" goto"),
+ SwitchInt { discr, .. } => {
+ format!(" switchInt(_{})", pretty_operand(discr))
+ }
+ Resume => format!(" resume"),
+ Abort => format!(" abort"),
+ Return => format!(" return"),
+ Unreachable => format!(" unreachable"),
+ Drop { place, .. } => format!(" drop(_{:?})", place.local),
+ Call { func, args, destination, .. } => {
+ pretty.push_str(" ");
+ pretty.push_str(format!("_{} = ", destination.local).as_str());
+ pretty.push_str(&pretty_operand(func));
+ pretty.push_str("(");
+ args.iter().enumerate().for_each(|(i, arg)| {
+ if i > 0 {
+ pretty.push_str(", ");
+ }
+ pretty.push_str(&pretty_operand(arg));
+ });
+ pretty.push_str(")");
+ pretty
+ }
+ Assert { cond, expected, msg, target: _, unwind: _ } => {
+ pretty.push_str(" assert(");
+ if !expected {
+ pretty.push_str("!");
+ }
+ pretty.push_str(format!("{} bool),", &pretty_operand(cond)).as_str());
+ pretty.push_str(&pretty_assert_message(msg));
+ pretty.push_str(")");
+ pretty
+ }
+ InlineAsm { .. } => todo!(),
+ }
+}
+
+pub fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> {
+ use self::TerminatorKind::*;
+ match terminator {
+ Resume | Abort | Return | Unreachable => 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()],
+ Drop { unwind: _, .. } => vec!["return".into()],
+ Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
+ vec!["return".into(), "unwind".into()]
+ }
+ Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
+ Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
+ Call { target: None, unwind: _, .. } => vec![],
+ Assert { unwind: UnwindAction::Cleanup(_), .. } => {
+ vec!["success".into(), "unwind".into()]
+ }
+ Assert { unwind: _, .. } => vec!["success".into()],
+ InlineAsm { .. } => todo!(),
+ }
+}
+
+pub fn pretty_assert_message(msg: &AssertMessage) -> String {
+ let mut pretty = String::new();
+ match msg {
+ AssertMessage::BoundsCheck { len, index } => {
+ let pretty_len = pretty_operand(len);
+ let pretty_index = pretty_operand(index);
+ pretty.push_str(format!("\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}").as_str());
+ pretty
+ }
+ AssertMessage::Overflow(BinOp::Add, l, r) => {
+ let pretty_l = pretty_operand(l);
+ let pretty_r = pretty_operand(r);
+ pretty.push_str(format!("\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str());
+ pretty
+ }
+ AssertMessage::Overflow(BinOp::Sub, l, r) => {
+ let pretty_l = pretty_operand(l);
+ let pretty_r = pretty_operand(r);
+ pretty.push_str(format!("\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str());
+ pretty
+ }
+ AssertMessage::Overflow(BinOp::Mul, l, r) => {
+ let pretty_l = pretty_operand(l);
+ let pretty_r = pretty_operand(r);
+ pretty.push_str(format!("\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str());
+ pretty
+ }
+ AssertMessage::Overflow(BinOp::Div, l, r) => {
+ let pretty_l = pretty_operand(l);
+ let pretty_r = pretty_operand(r);
+ pretty.push_str(format!("\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str());
+ pretty
+ }
+ AssertMessage::Overflow(BinOp::Rem, l, r) => {
+ let pretty_l = pretty_operand(l);
+ let pretty_r = pretty_operand(r);
+ pretty.push_str(format!("\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str());
+ pretty
+ }
+ AssertMessage::Overflow(BinOp::Shr, _, r) => {
+ let pretty_r = pretty_operand(r);
+ pretty.push_str(
+ format!("\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}")
+ .as_str(),
+ );
+ pretty
+ }
+ AssertMessage::Overflow(BinOp::Shl, _, r) => {
+ let pretty_r = pretty_operand(r);
+ pretty.push_str(
+ format!("\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}")
+ .as_str(),
+ );
+ pretty
+ }
+ AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op),
+ AssertMessage::OverflowNeg(op) => {
+ let pretty_op = pretty_operand(op);
+ pretty.push_str(
+ format!("\"attempt to negate `{{}}`, which would overflow\", {pretty_op}").as_str(),
+ );
+ pretty
+ }
+ AssertMessage::DivisionByZero(op) => {
+ let pretty_op = pretty_operand(op);
+ pretty.push_str(format!("\"attempt to divide `{{}}` by zero\", {pretty_op}").as_str());
+ pretty
+ }
+ AssertMessage::RemainderByZero(op) => {
+ let pretty_op = pretty_operand(op);
+ pretty.push_str(
+ format!("\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}").as_str(),
+ );
+ pretty
+ }
+ AssertMessage::MisalignedPointerDereference { required, found } => {
+ let pretty_required = pretty_operand(required);
+ let pretty_found = pretty_operand(found);
+ pretty.push_str(format!("\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}").as_str());
+ pretty
+ }
+ AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => {
+ msg.description().unwrap().to_string()
+ }
+ }
+}
+
+pub fn pretty_operand(operand: &Operand) -> String {
+ let mut pretty = String::new();
+ match operand {
+ Operand::Copy(copy) => {
+ pretty.push_str("");
+ pretty.push_str(format!("{}", copy.local).as_str());
+ }
+ Operand::Move(mv) => {
+ pretty.push_str("move ");
+ pretty.push_str(format!("_{}", mv.local).as_str());
+ }
+ Operand::Constant(cnst) => {
+ pretty.push_str("const ");
+ pretty.push_str(with(|cx| cx.const_literal(&cnst.literal)).as_str());
+ }
+ }
+ pretty
+}
+
+pub fn pretty_rvalue(rval: &Rvalue) -> String {
+ let mut pretty = String::new();
+ match rval {
+ Rvalue::AddressOf(muta, addr) => {
+ pretty.push_str("&raw ");
+ pretty.push_str(&ret_mutability(muta));
+ pretty.push_str(format!("(*_{})", addr.local).as_str());
+ }
+ Rvalue::Aggregate(aggregatekind, operands) => {
+ pretty.push_str(format!("{:#?}", aggregatekind).as_str());
+ pretty.push_str("(");
+ operands.iter().enumerate().for_each(|(i, op)| {
+ pretty.push_str(&pretty_operand(op));
+ if i != operands.len() - 1 {
+ pretty.push_str(", ");
+ }
+ });
+ pretty.push_str(")");
+ }
+ Rvalue::BinaryOp(bin, op, op2) => {
+ pretty.push_str(&pretty_operand(op));
+ pretty.push_str(" ");
+ pretty.push_str(format!("{:#?}", bin).as_str());
+ pretty.push_str(" ");
+ pretty.push_str(&pretty_operand(op2));
+ }
+ Rvalue::Cast(_, op, ty) => {
+ pretty.push_str(&pretty_operand(op));
+ pretty.push_str(" as ");
+ pretty.push_str(&pretty_ty(ty.kind()));
+ }
+ Rvalue::CheckedBinaryOp(bin, op1, op2) => {
+ pretty.push_str(&pretty_operand(op1));
+ pretty.push_str(" ");
+ pretty.push_str(format!("{:#?}", bin).as_str());
+ pretty.push_str(" ");
+ pretty.push_str(&pretty_operand(op2));
+ }
+ Rvalue::CopyForDeref(deref) => {
+ pretty.push_str("CopyForDeref");
+ pretty.push_str(format!("{}", deref.local).as_str());
+ }
+ Rvalue::Discriminant(place) => {
+ pretty.push_str("discriminant");
+ pretty.push_str(format!("{}", place.local).as_str());
+ }
+ Rvalue::Len(len) => {
+ pretty.push_str("len");
+ pretty.push_str(format!("{}", len.local).as_str());
+ }
+ Rvalue::Ref(_, borrowkind, place) => {
+ pretty.push_str("ref");
+ pretty.push_str(format!("{:#?}", borrowkind).as_str());
+ pretty.push_str(format!("{}", place.local).as_str());
+ }
+ Rvalue::Repeat(op, cnst) => {
+ pretty.push_str(&pretty_operand(op));
+ pretty.push_str(" ");
+ pretty.push_str(&pretty_ty(cnst.ty().kind()));
+ }
+ Rvalue::ShallowInitBox(_, _) => (),
+ Rvalue::ThreadLocalRef(item) => {
+ pretty.push_str("thread_local_ref");
+ pretty.push_str(format!("{:#?}", item).as_str());
+ }
+ Rvalue::NullaryOp(nul, ty) => {
+ pretty.push_str(format!("{:#?}", nul).as_str());
+ pretty.push_str(&pretty_ty(ty.kind()));
+ pretty.push_str(" ");
+ }
+ Rvalue::UnaryOp(un, op) => {
+ pretty.push_str(&pretty_operand(op));
+ pretty.push_str(" ");
+ pretty.push_str(format!("{:#?}", un).as_str());
+ }
+ Rvalue::Use(op) => pretty.push_str(&pretty_operand(op)),
+ }
+ pretty
+}
+
+pub fn pretty_ty(ty: TyKind) -> String {
+ let mut pretty = String::new();
+ match ty {
+ TyKind::RigidTy(rigid_ty) => match rigid_ty {
+ RigidTy::Bool => "bool".to_string(),
+ RigidTy::Char => "char".to_string(),
+ RigidTy::Int(i) => match i {
+ IntTy::Isize => "isize".to_string(),
+ IntTy::I8 => "i8".to_string(),
+ IntTy::I16 => "i16".to_string(),
+ IntTy::I32 => "i32".to_string(),
+ IntTy::I64 => "i64".to_string(),
+ IntTy::I128 => "i128".to_string(),
+ },
+ RigidTy::Uint(u) => match u {
+ UintTy::Usize => "usize".to_string(),
+ UintTy::U8 => "u8".to_string(),
+ UintTy::U16 => "u16".to_string(),
+ UintTy::U32 => "u32".to_string(),
+ UintTy::U64 => "u64".to_string(),
+ UintTy::U128 => "u128".to_string(),
+ },
+ RigidTy::Float(f) => match f {
+ FloatTy::F32 => "f32".to_string(),
+ FloatTy::F64 => "f64".to_string(),
+ },
+ RigidTy::Adt(def, _) => {
+ format!("{:#?}", with(|cx| cx.def_ty(def.0)))
+ }
+ RigidTy::Str => "str".to_string(),
+ RigidTy::Array(ty, len) => {
+ format!("[{}; {}]", pretty_ty(ty.kind()), with(|cx| cx.const_literal(&len)))
+ }
+ RigidTy::Slice(ty) => {
+ format!("[{}]", pretty_ty(ty.kind()))
+ }
+ RigidTy::RawPtr(ty, mutability) => {
+ pretty.push_str("*");
+ match mutability {
+ Mutability::Not => pretty.push_str("const "),
+ Mutability::Mut => pretty.push_str("mut "),
+ }
+ pretty.push_str(&pretty_ty(ty.kind()));
+ pretty
+ }
+ RigidTy::Ref(_, ty, mutability) => match mutability {
+ Mutability::Not => format!("&{}", pretty_ty(ty.kind())),
+ Mutability::Mut => format!("&mut {}", pretty_ty(ty.kind())),
+ },
+ RigidTy::FnDef(_, _) => format!("{:#?}", rigid_ty),
+ RigidTy::FnPtr(_) => format!("{:#?}", rigid_ty),
+ RigidTy::Closure(_, _) => format!("{:#?}", rigid_ty),
+ RigidTy::Coroutine(_, _, _) => format!("{:#?}", rigid_ty),
+ RigidTy::Dynamic(data, region, repr) => {
+ // FIXME: Fix binder printing, it looks ugly now
+ pretty.push_str("(");
+ match repr {
+ DynKind::Dyn => pretty.push_str("dyn "),
+ DynKind::DynStar => pretty.push_str("dyn* "),
+ }
+ pretty.push_str(format!("{:#?}", data).as_str());
+ pretty.push_str(format!(" + {:#?} )", region).as_str());
+ pretty
+ }
+ RigidTy::Never => "!".to_string(),
+ RigidTy::Tuple(tuple) => {
+ if tuple.is_empty() {
+ "()".to_string()
+ } else {
+ let mut tuple_str = String::new();
+ tuple_str.push_str("(");
+ tuple.iter().enumerate().for_each(|(i, ty)| {
+ tuple_str.push_str(&pretty_ty(ty.kind()));
+ if i != tuple.len() - 1 {
+ tuple_str.push_str(", ");
+ }
+ });
+ tuple_str.push_str(")");
+ tuple_str
+ }
+ }
+ _ => format!("{:#?}", rigid_ty),
+ },
+ TyKind::Alias(_, _) => format!("{:#?}", ty),
+ TyKind::Param(param_ty) => {
+ format!("{:#?}", param_ty.name)
+ }
+ TyKind::Bound(_, _) => format!("{:#?}", ty),
+ }
+}
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
index 806dced71..ab57ff0f8 100644
--- a/compiler/stable_mir/src/mir/visit.rs
+++ b/compiler/stable_mir/src/mir/visit.rs
@@ -76,6 +76,15 @@ pub trait MirVisitor {
self.super_place(place, ptx, location)
}
+ fn visit_projection_elem(
+ &mut self,
+ elem: &ProjectionElem,
+ ptx: PlaceContext,
+ location: Location,
+ ) {
+ self.super_projection_elem(elem, ptx, location);
+ }
+
fn visit_local(&mut self, local: &Local, ptx: PlaceContext, location: Location) {
let _ = (local, ptx, location);
}
@@ -119,8 +128,12 @@ pub trait MirVisitor {
self.super_assert_msg(msg, location)
}
+ fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo) {
+ self.super_var_debug_info(var_debug_info);
+ }
+
fn super_body(&mut self, body: &Body) {
- let Body { blocks, locals: _, arg_count } = body;
+ let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = body;
for bb in blocks {
self.visit_basic_block(bb);
@@ -133,9 +146,15 @@ pub trait MirVisitor {
}
let local_start = arg_count + 1;
- for (idx, arg) in body.arg_locals().iter().enumerate() {
+ 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)
}
fn super_basic_block(&mut self, bb: &BasicBlock) {
@@ -148,7 +167,7 @@ pub trait MirVisitor {
fn super_local_decl(&mut self, local: Local, decl: &LocalDecl) {
let _ = local;
- let LocalDecl { ty, span } = decl;
+ let LocalDecl { ty, span, .. } = decl;
self.visit_ty(ty, Location(*span));
}
@@ -215,13 +234,12 @@ pub trait MirVisitor {
fn super_terminator(&mut self, term: &Terminator, location: Location) {
let Terminator { kind, span } = term;
- self.visit_span(&span);
+ self.visit_span(span);
match kind {
TerminatorKind::Goto { .. }
| TerminatorKind::Resume
| TerminatorKind::Abort
- | TerminatorKind::Unreachable
- | TerminatorKind::CoroutineDrop => {}
+ | TerminatorKind::Unreachable => {}
TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
self.visit_operand(cond, location);
self.visit_assert_msg(msg, location);
@@ -251,7 +269,7 @@ pub trait MirVisitor {
let local = RETURN_LOCAL;
self.visit_local(&local, PlaceContext::NON_MUTATING, location);
}
- TerminatorKind::SwitchInt { discr, targets: _, otherwise: _ } => {
+ TerminatorKind::SwitchInt { discr, targets: _ } => {
self.visit_operand(discr, location);
}
}
@@ -264,7 +282,29 @@ pub trait MirVisitor {
fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
let _ = location;
let _ = ptx;
- visit_opaque(&Opaque(place.projection.clone()));
+ self.visit_local(&place.local, ptx, location);
+
+ for elem in &place.projection {
+ self.visit_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),
+ }
}
fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) {
@@ -351,6 +391,24 @@ pub trait MirVisitor {
let _ = args;
}
+ fn super_var_debug_info(&mut self, var_debug_info: &VarDebugInfo) {
+ let VarDebugInfo { source_info, composite, value, name: _, argument_index: _ } =
+ var_debug_info;
+ self.visit_span(&source_info.span);
+ let location = Location(source_info.span);
+ if let Some(composite) = composite {
+ self.visit_ty(&composite.ty, location);
+ }
+ match value {
+ VarDebugInfoContents::Place(place) => {
+ self.visit_place(place, PlaceContext::NON_USE, location);
+ }
+ VarDebugInfoContents::Const(constant) => {
+ self.visit_const(&constant.const_, location);
+ }
+ }
+ }
+
fn super_assert_msg(&mut self, msg: &AssertMessage, location: Location) {
match msg {
AssertMessage::BoundsCheck { len, index } => {
@@ -386,7 +444,7 @@ pub trait MirVisitor {
fn visit_opaque(_: &Opaque) {}
/// The location of a statement / terminator in the code and the CFG.
-#[derive(Clone, Copy, PartialEq, Eq)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Location(Span);
impl Location {
@@ -396,7 +454,7 @@ impl Location {
}
/// Information about a place's usage.
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[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.
diff --git a/compiler/stable_mir/src/target.rs b/compiler/stable_mir/src/target.rs
new file mode 100644
index 000000000..41ec205cf
--- /dev/null
+++ b/compiler/stable_mir/src/target.rs
@@ -0,0 +1,50 @@
+//! Provide information about the machine that this is being compiled into.
+
+use crate::compiler_interface::with;
+
+/// The properties of the target machine being compiled into.
+#[derive(Clone, PartialEq, Eq)]
+pub struct MachineInfo {
+ pub endian: Endian,
+ pub pointer_width: MachineSize,
+}
+
+impl MachineInfo {
+ pub fn target() -> MachineInfo {
+ with(|cx| cx.target_info())
+ }
+
+ pub fn target_endianess() -> 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)]
+pub enum Endian {
+ Little,
+ Big,
+}
+
+/// Represent the size of a component.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct MachineSize {
+ num_bits: usize,
+}
+
+impl MachineSize {
+ pub fn bytes(self) -> usize {
+ self.num_bits / 8
+ }
+
+ pub fn bits(self) -> usize {
+ self.num_bits
+ }
+
+ pub fn from_bits(num_bits: usize) -> MachineSize {
+ MachineSize { num_bits }
+ }
+}
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
index 5dfaa0fd8..1d4d7b6d3 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/stable_mir/src/ty.rs
@@ -1,10 +1,15 @@
use super::{
mir::Safety,
mir::{Body, Mutability},
- with, AllocId, DefId, Symbol,
+ with, DefId, Error, Symbol,
};
+use crate::abi::Layout;
+use crate::crate_def::CrateDef;
+use crate::mir::alloc::{read_target_int, read_target_uint, AllocId};
+use crate::target::MachineInfo;
use crate::{Filename, Opaque};
-use std::fmt::{self, Debug, Formatter};
+use std::fmt::{self, Debug, Display, Formatter};
+use std::ops::Range;
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Ty(pub usize);
@@ -15,6 +20,79 @@ impl Debug for Ty {
}
}
+/// 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<Ty, Error> {
+ Ok(Ty::from_rigid_kind(RigidTy::Array(elem_ty, Const::try_from_target_usize(size)?)))
+ }
+
+ /// Create a new array type from Const length.
+ pub fn new_array_with_const_len(elem_ty: Ty, len: Const) -> 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 box type that represents `Box<T>`, 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<Layout, Error> {
+ with(|cx| cx.ty_layout(self))
+ }
+}
+
impl Ty {
pub fn kind(&self) -> TyKind {
with(|context| context.ty_kind(*self))
@@ -47,6 +125,16 @@ impl Const {
pub fn ty(&self) -> Ty {
self.ty
}
+
+ /// Creates an interned usize constant.
+ fn try_from_target_usize(val: u64) -> Result<Self, Error> {
+ with(|cx| cx.usize_to_const(val))
+ }
+
+ /// Try to evaluate to a target `usize`.
+ pub fn eval_target_usize(&self) -> Result<u64, Error> {
+ with(|cx| cx.eval_target_usize(self))
+ }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -61,8 +149,8 @@ pub struct Region {
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RegionKind {
- ReEarlyBound(EarlyBoundRegion),
- ReLateBound(DebruijnIndex, BoundRegion),
+ ReEarlyParam(EarlyParamRegion),
+ ReBound(DebruijnIndex, BoundRegion),
ReStatic,
RePlaceholder(Placeholder<BoundRegion>),
ReErased,
@@ -71,7 +159,7 @@ pub enum RegionKind {
pub(crate) type DebruijnIndex = u32;
#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct EarlyBoundRegion {
+pub struct EarlyParamRegion {
pub def_id: RegionDef,
pub index: u32,
pub name: Symbol,
@@ -113,7 +201,7 @@ impl Span {
/// Return lines that corespond to this `Span`
pub fn get_lines(&self) -> LineInfo {
- with(|c| c.get_lines(&self))
+ with(|c| c.get_lines(self))
}
}
@@ -135,6 +223,226 @@ pub enum TyKind {
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<Binder<ExistentialTraitRef>> {
+ 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<Ty> {
+ 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 unsafe ptrs -- can only be dereferenced explicitly.
+ pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut> {
+ 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<PolyFnSig> {
+ 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<Ty> {
+ self.rigid().map(|ty| with(|cx| cx.rigid_ty_discriminant_ty(ty)))
+ }
+}
+
+pub struct TypeAndMut {
+ pub ty: Ty,
+ pub mutability: Mutability,
+}
+
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RigidTy {
Bool,
@@ -156,6 +464,20 @@ pub enum RigidTy {
Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind),
Never,
Tuple(Vec<Ty>),
+ 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<RigidTy> for TyKind {
+ fn from(value: RigidTy) -> Self {
+ TyKind::RigidTy(value)
+ }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -168,6 +490,19 @@ pub enum IntTy {
I128,
}
+impl IntTy {
+ pub fn num_bytes(self) -> usize {
+ match self {
+ IntTy::Isize => crate::target::MachineInfo::target_pointer_width().bytes().into(),
+ IntTy::I8 => 1,
+ IntTy::I16 => 2,
+ IntTy::I32 => 4,
+ IntTy::I64 => 8,
+ IntTy::I128 => 16,
+ }
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UintTy {
Usize,
@@ -178,6 +513,19 @@ pub enum UintTy {
U128,
}
+impl UintTy {
+ pub fn num_bytes(self) -> usize {
+ match self {
+ UintTy::Usize => crate::target::MachineInfo::target_pointer_width().bytes().into(),
+ UintTy::U8 => 1,
+ UintTy::U16 => 2,
+ UintTy::U32 => 4,
+ UintTy::U64 => 8,
+ UintTy::U128 => 16,
+ }
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FloatTy {
F32,
@@ -190,50 +538,203 @@ pub enum Movability {
Movable,
}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct ForeignDef(pub DefId);
+crate_def! {
+ /// Hold information about a ForeignItem in a crate.
+ pub ForeignDef;
+}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct FnDef(pub DefId);
+crate_def! {
+ /// Hold information about a function definition in a crate.
+ pub FnDef;
+}
impl FnDef {
- pub fn body(&self) -> Body {
- with(|ctx| ctx.mir_body(self.0))
+ // Get the function body if available.
+ pub fn body(&self) -> Option<Body> {
+ with(|ctx| ctx.has_body(self.0).then(|| ctx.mir_body(self.0)))
}
}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct ClosureDef(pub DefId);
+crate_def! {
+ pub ClosureDef;
+}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct CoroutineDef(pub DefId);
+crate_def! {
+ pub CoroutineDef;
+}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct ParamDef(pub DefId);
+crate_def! {
+ pub ParamDef;
+}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct BrNamedDef(pub DefId);
+crate_def! {
+ pub BrNamedDef;
+}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct AdtDef(pub DefId);
+crate_def! {
+ pub AdtDef;
+}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct AliasDef(pub DefId);
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+pub enum AdtKind {
+ Enum,
+ Union,
+ Struct,
+}
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct TraitDef(pub DefId);
+impl AdtDef {
+ pub fn kind(&self) -> AdtKind {
+ with(|cx| cx.adt_kind(*self))
+ }
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct GenericDef(pub DefId);
+ /// Retrieve the type of this Adt.
+ pub fn ty(&self) -> Ty {
+ with(|cx| cx.def_ty(self.0))
+ }
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct ConstDef(pub DefId);
+ /// Retrieve the type of this Adt instantiating the type 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))
+ }
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct ImplDef(pub DefId);
+ pub fn is_box(&self) -> bool {
+ with(|cx| cx.adt_is_box(*self))
+ }
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct RegionDef(pub DefId);
+ 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<VariantDef> {
+ self.variants_iter().collect()
+ }
+
+ /// Iterate over the variants in this ADT.
+ pub fn variants_iter(&self) -> impl Iterator<Item = VariantDef> + '_ {
+ (0..self.num_variants())
+ .map(|idx| VariantDef { idx: VariantIdx::to_val(idx), adt_def: *self })
+ }
+
+ pub fn variant(&self, idx: VariantIdx) -> Option<VariantDef> {
+ (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)]
+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<FieldDef> {
+ with(|cx| cx.variant_fields(*self))
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+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 the type 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! {
+ pub AliasDef;
+}
+
+crate_def! {
+ pub TraitDef;
+}
+
+crate_def! {
+ pub GenericDef;
+}
+
+crate_def! {
+ pub ConstDef;
+}
+
+crate_def! {
+ pub ImplDef;
+}
+
+crate_def! {
+ pub RegionDef;
+}
+
+crate_def! {
+ pub CoroutineWitnessDef;
+}
/// A list of generic arguments.
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -282,6 +783,14 @@ impl GenericArgKind {
_ => 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)]
@@ -314,6 +823,16 @@ pub struct FnSig {
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)]
pub enum Abi {
Rust,
@@ -345,12 +864,47 @@ pub enum Abi {
RiscvInterruptS,
}
+/// A binder represents a possibly generic type and its bound vars.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Binder<T> {
pub value: T,
pub bound_vars: Vec<BoundVariableKind>,
}
+impl<T> Binder<T> {
+ /// Create a new binder with the given bound vars.
+ pub fn bind_with_vars(value: T, bound_vars: Vec<BoundVariableKind>) -> 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<F, U>(&self, f: F) -> Binder<U>
+ 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<F, U>(self, f: F) -> Binder<U>
+ 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)]
pub struct EarlyBinder<T> {
pub value: T,
@@ -389,12 +943,27 @@ pub enum ExistentialPredicate {
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)]
pub struct ExistentialTraitRef {
pub def_id: TraitDef,
pub generic_args: GenericArgs,
}
+impl Binder<ExistentialTraitRef> {
+ pub fn with_self_ty(&self, self_ty: Ty) -> Binder<TraitRef> {
+ 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)]
pub struct ExistentialProjection {
pub def_id: TraitDef,
@@ -417,21 +986,21 @@ pub struct BoundTy {
pub type Bytes = Vec<Option<u8>>;
pub type Size = usize;
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
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, Eq, PartialEq)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
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)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Allocation {
pub bytes: Bytes,
pub provenance: ProvenanceMap,
@@ -439,6 +1008,74 @@ pub struct Allocation {
pub mutability: Mutability,
}
+impl Allocation {
+ /// Get a vector of bytes for an Allocation that has been fully initialized
+ pub fn raw_bytes(&self) -> Result<Vec<u8>, Error> {
+ self.bytes
+ .iter()
+ .copied()
+ .collect::<Option<Vec<_>>>()
+ .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<usize>) -> Result<u128, Error> {
+ 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::<Option<Vec<_>>>()
+ .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<u128, Error> {
+ 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<i128, Error> {
+ 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<bool, Error> {
+ 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<bool, Error> {
+ 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)]
pub enum ConstantKind {
Allocated(Allocation),
@@ -500,10 +1137,39 @@ impl TraitDecl {
pub type ImplTrait = EarlyBinder<TraitRef>;
+/// A complete reference to a trait, i.e., one where `Self` is known.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TraitRef {
pub def_id: TraitDef,
- pub args: GenericArgs,
+ /// 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<TraitRef, ()> {
+ 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)]
@@ -542,7 +1208,6 @@ pub struct GenericPredicates {
pub enum PredicateKind {
Clause(ClauseKind),
ObjectSafe(TraitDef),
- ClosureKind(ClosureDef, GenericArgs, ClosureKind),
SubType(SubtypePredicate),
Coerce(CoercePredicate),
ConstEquate(Const, Const),
@@ -633,3 +1298,21 @@ macro_rules! index_impl {
index_impl!(ConstId);
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)]
+pub struct VariantIdx(usize);
+
+index_impl!(VariantIdx);
diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs
index 05e0b9b4d..65e42879d 100644
--- a/compiler/stable_mir/src/visitor.rs
+++ b/compiler/stable_mir/src/visitor.rs
@@ -149,6 +149,7 @@ impl Visitable for RigidTy {
RigidTy::FnPtr(sig) => sig.visit(visitor),
RigidTy::Closure(_, args) => args.visit(visitor),
RigidTy::Coroutine(_, args, _) => args.visit(visitor),
+ RigidTy::CoroutineWitness(_, args) => args.visit(visitor),
RigidTy::Dynamic(pred, r, _) => {
pred.visit(visitor)?;
r.visit(visitor)