2023-03-13 08:42:24 +00:00
|
|
|
|
//! MIR definitions and implementation
|
|
|
|
|
|
2023-09-08 09:09:41 +00:00
|
|
|
|
use std::{collections::hash_map::Entry, fmt::Display, iter};
|
2023-03-13 08:42:24 +00:00
|
|
|
|
|
|
|
|
|
use crate::{
|
2023-07-17 13:49:15 +00:00
|
|
|
|
consteval::usize_const,
|
|
|
|
|
db::HirDatabase,
|
|
|
|
|
display::HirDisplay,
|
|
|
|
|
infer::{normalize, PointerCast},
|
|
|
|
|
lang_items::is_box,
|
|
|
|
|
mapping::ToChalk,
|
|
|
|
|
CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
|
|
|
|
|
Substitution, TraitEnvironment, Ty, TyKind,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
};
|
|
|
|
|
use base_db::CrateId;
|
|
|
|
|
use chalk_ir::Mutability;
|
2024-01-06 14:04:58 +00:00
|
|
|
|
use either::Either;
|
2023-03-13 08:42:24 +00:00
|
|
|
|
use hir_def::{
|
2024-08-22 07:39:20 +00:00
|
|
|
|
body::Body,
|
|
|
|
|
hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
|
2024-01-06 14:04:58 +00:00
|
|
|
|
DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
};
|
|
|
|
|
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
|
|
|
|
|
2024-01-26 19:08:10 +00:00
|
|
|
|
mod borrowck;
|
2023-03-13 08:42:24 +00:00
|
|
|
|
mod eval;
|
|
|
|
|
mod lower;
|
|
|
|
|
mod monomorphization;
|
2024-01-26 19:08:10 +00:00
|
|
|
|
mod pretty;
|
2023-03-13 08:42:24 +00:00
|
|
|
|
|
|
|
|
|
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
|
2023-07-17 13:49:15 +00:00
|
|
|
|
pub use eval::{
|
|
|
|
|
interpret_mir, pad16, render_const_using_debug_impl, Evaluator, MirEvalError, VTableMap,
|
|
|
|
|
};
|
2023-03-13 08:42:24 +00:00
|
|
|
|
pub use lower::{
|
|
|
|
|
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
|
2023-06-05 09:04:23 +00:00
|
|
|
|
};
|
2023-03-13 08:42:24 +00:00
|
|
|
|
pub use monomorphization::{
|
|
|
|
|
monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query,
|
|
|
|
|
monomorphized_mir_body_query, monomorphized_mir_body_recover,
|
|
|
|
|
};
|
2023-09-08 09:09:41 +00:00
|
|
|
|
use rustc_hash::FxHashMap;
|
2023-03-13 08:42:24 +00:00
|
|
|
|
use smallvec::{smallvec, SmallVec};
|
|
|
|
|
use stdx::{impl_from, never};
|
|
|
|
|
|
|
|
|
|
use super::consteval::{intern_const_scalar, try_const_usize};
|
|
|
|
|
|
|
|
|
|
pub type BasicBlockId = Idx<BasicBlock>;
|
|
|
|
|
pub type LocalId = Idx<Local>;
|
|
|
|
|
|
|
|
|
|
fn return_slot() -> LocalId {
|
|
|
|
|
LocalId::from_raw(RawIdx::from(0))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct Local {
|
|
|
|
|
pub ty: Ty,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of
|
|
|
|
|
/// the memory model. One proposal for a definition of values can be found [on UCG][value-def].
|
|
|
|
|
///
|
|
|
|
|
/// [value-def]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/value-domain.md
|
|
|
|
|
///
|
|
|
|
|
/// The most common way to create values is via loading a place. Loading a place is an operation
|
|
|
|
|
/// which reads the memory of the place and converts it to a value. This is a fundamentally *typed*
|
|
|
|
|
/// operation. The nature of the value produced depends on the type of the conversion. Furthermore,
|
|
|
|
|
/// there may be other effects: if the type has a validity constraint loading the place might be UB
|
|
|
|
|
/// if the validity constraint is not met.
|
|
|
|
|
///
|
|
|
|
|
/// **Needs clarification:** Ralf proposes that loading a place not have side-effects.
|
|
|
|
|
/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this
|
|
|
|
|
/// something we can even decide without knowing more about Rust's memory model?
|
|
|
|
|
///
|
|
|
|
|
/// **Needs clarification:** Is loading a place that has its variant index set well-formed? Miri
|
|
|
|
|
/// currently implements it, but it seems like this may be something to check against in the
|
|
|
|
|
/// validator.
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum Operand {
|
|
|
|
|
/// Creates a value by loading the given place.
|
|
|
|
|
///
|
|
|
|
|
/// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
|
|
|
|
|
/// is no such requirement.
|
|
|
|
|
Copy(Place),
|
|
|
|
|
|
|
|
|
|
/// Creates a value by performing loading the place, just like the `Copy` operand.
|
|
|
|
|
///
|
|
|
|
|
/// This *may* additionally overwrite the place with `uninit` bytes, depending on how we decide
|
|
|
|
|
/// in [UCG#188]. You should not emit MIR that may attempt a subsequent second load of this
|
|
|
|
|
/// place without first re-initializing it.
|
|
|
|
|
///
|
|
|
|
|
/// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
|
|
|
|
|
Move(Place),
|
|
|
|
|
/// Constants are already semantically values, and remain unchanged.
|
|
|
|
|
Constant(Const),
|
|
|
|
|
/// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
|
|
|
|
|
/// handles it with the `Constant` variant somehow.
|
|
|
|
|
Static(StaticId),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Operand {
|
2024-01-06 16:48:07 +00:00
|
|
|
|
fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap, ty: Ty) -> Self {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Operand::Constant(intern_const_scalar(ConstScalar::Bytes(data, memory_map), ty))
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-06 16:48:07 +00:00
|
|
|
|
fn from_bytes(data: Box<[u8]>, ty: Ty) -> Self {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Operand::from_concrete_const(data, MemoryMap::default(), ty)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn const_zst(ty: Ty) -> Operand {
|
2024-01-06 16:48:07 +00:00
|
|
|
|
Self::from_bytes(Box::default(), ty)
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
2023-06-05 09:04:23 +00:00
|
|
|
|
|
|
|
|
|
fn from_fn(
|
|
|
|
|
db: &dyn HirDatabase,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
func_id: hir_def::FunctionId,
|
|
|
|
|
generic_args: Substitution,
|
|
|
|
|
) -> Operand {
|
2023-06-05 09:04:23 +00:00
|
|
|
|
let ty =
|
2023-03-13 08:42:24 +00:00
|
|
|
|
chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args)
|
|
|
|
|
.intern(Interner);
|
2024-01-06 16:48:07 +00:00
|
|
|
|
Operand::from_bytes(Box::default(), ty)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
|
pub enum ProjectionElem<V, T> {
|
|
|
|
|
Deref,
|
2024-01-06 14:04:58 +00:00
|
|
|
|
Field(Either<FieldId, TupleFieldId>),
|
2023-03-13 08:42:24 +00:00
|
|
|
|
// FIXME: get rid of this, and use FieldId for tuples and closures
|
2024-01-06 14:04:58 +00:00
|
|
|
|
ClosureField(usize),
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Index(V),
|
|
|
|
|
ConstantIndex { offset: u64, from_end: bool },
|
|
|
|
|
Subslice { from: u64, to: u64 },
|
|
|
|
|
//Downcast(Option<Symbol>, VariantIdx),
|
|
|
|
|
OpaqueCast(T),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<V, T> ProjectionElem<V, T> {
|
|
|
|
|
pub fn projected_ty(
|
2023-06-05 09:04:23 +00:00
|
|
|
|
&self,
|
2023-07-17 13:49:15 +00:00
|
|
|
|
mut base: Ty,
|
2023-06-05 09:04:23 +00:00
|
|
|
|
db: &dyn HirDatabase,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
|
2023-06-05 09:04:23 +00:00
|
|
|
|
krate: CrateId,
|
|
|
|
|
) -> Ty {
|
2023-07-24 09:21:34 +00:00
|
|
|
|
if matches!(base.kind(Interner), TyKind::Alias(_) | TyKind::AssociatedType(..)) {
|
2023-07-17 13:49:15 +00:00
|
|
|
|
base = normalize(
|
|
|
|
|
db,
|
|
|
|
|
// FIXME: we should get this from caller
|
2023-12-08 09:47:36 +00:00
|
|
|
|
TraitEnvironment::empty(krate),
|
2023-07-17 13:49:15 +00:00
|
|
|
|
base,
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
match self {
|
2023-07-24 09:21:34 +00:00
|
|
|
|
ProjectionElem::Deref => match &base.kind(Interner) {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
|
|
|
|
|
TyKind::Adt(adt, subst) if is_box(db, adt.0) => {
|
|
|
|
|
subst.at(Interner, 0).assert_ty_ref(Interner).clone()
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
_ => {
|
2024-08-15 23:36:24 +00:00
|
|
|
|
never!(
|
|
|
|
|
"Overloaded deref on type {} is not a projection",
|
|
|
|
|
base.display(db, db.crate_graph()[krate].edition)
|
|
|
|
|
);
|
2024-01-18 12:59:49 +00:00
|
|
|
|
TyKind::Error.intern(Interner)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2024-01-06 14:04:58 +00:00
|
|
|
|
ProjectionElem::Field(Either::Left(f)) => match &base.kind(Interner) {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TyKind::Adt(_, subst) => {
|
|
|
|
|
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-09-09 08:45:29 +00:00
|
|
|
|
ty => {
|
|
|
|
|
never!("Only adt has field, found {:?}", ty);
|
2024-01-18 12:59:49 +00:00
|
|
|
|
TyKind::Error.intern(Interner)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2024-01-06 14:04:58 +00:00
|
|
|
|
ProjectionElem::Field(Either::Right(f)) => match &base.kind(Interner) {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TyKind::Tuple(_, subst) => subst
|
|
|
|
|
.as_slice(Interner)
|
2024-01-06 14:04:58 +00:00
|
|
|
|
.get(f.index as usize)
|
2023-03-13 08:42:24 +00:00
|
|
|
|
.map(|x| x.assert_ty_ref(Interner))
|
2023-06-05 09:04:23 +00:00
|
|
|
|
.cloned()
|
2023-03-13 08:42:24 +00:00
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
|
never!("Out of bound tuple field");
|
|
|
|
|
TyKind::Error.intern(Interner)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}),
|
2024-09-06 12:44:05 +00:00
|
|
|
|
ty => {
|
|
|
|
|
never!("Only tuple has tuple field: {:?}", ty);
|
2024-01-18 12:59:49 +00:00
|
|
|
|
TyKind::Error.intern(Interner)
|
2024-01-06 14:04:58 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
ProjectionElem::ClosureField(f) => match &base.kind(Interner) {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
_ => {
|
2024-01-06 14:04:58 +00:00
|
|
|
|
never!("Only closure has closure field");
|
2024-01-18 12:59:49 +00:00
|
|
|
|
TyKind::Error.intern(Interner)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2023-03-13 08:42:24 +00:00
|
|
|
|
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => {
|
2023-07-24 09:21:34 +00:00
|
|
|
|
match &base.kind(Interner) {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
_ => {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
never!("Overloaded index is not a projection");
|
2024-01-18 12:59:49 +00:00
|
|
|
|
TyKind::Error.intern(Interner)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-24 09:21:34 +00:00
|
|
|
|
&ProjectionElem::Subslice { from, to } => match &base.kind(Interner) {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TyKind::Array(inner, c) => {
|
|
|
|
|
let next_c = usize_const(
|
2023-06-05 09:04:23 +00:00
|
|
|
|
db,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
match try_const_usize(db, c) {
|
|
|
|
|
None => None,
|
|
|
|
|
Some(x) => x.checked_sub(u128::from(from + to)),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
},
|
|
|
|
|
krate,
|
|
|
|
|
);
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TyKind::Array(inner.clone(), next_c).intern(Interner)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TyKind::Slice(_) => base.clone(),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
_ => {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
never!("Subslice projection should only happen on slice and array");
|
2024-01-18 12:59:49 +00:00
|
|
|
|
TyKind::Error.intern(Interner)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2023-03-13 08:42:24 +00:00
|
|
|
|
ProjectionElem::OpaqueCast(_) => {
|
|
|
|
|
never!("We don't emit these yet");
|
2024-01-18 12:59:49 +00:00
|
|
|
|
TyKind::Error.intern(Interner)
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 08:42:24 +00:00
|
|
|
|
type PlaceElem = ProjectionElem<LocalId, Ty>;
|
|
|
|
|
|
2023-09-08 09:09:41 +00:00
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
|
pub struct ProjectionId(u32);
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct ProjectionStore {
|
|
|
|
|
id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem]>>,
|
|
|
|
|
proj_to_id: FxHashMap<Box<[PlaceElem]>, ProjectionId>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for ProjectionStore {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() };
|
|
|
|
|
// Ensure that [] will get the id 0 which is used in `ProjectionId::Empty`
|
|
|
|
|
this.intern(Box::new([]));
|
|
|
|
|
this
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ProjectionStore {
|
2023-10-14 17:20:11 +00:00
|
|
|
|
pub fn shrink_to_fit(&mut self) {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
self.id_to_proj.shrink_to_fit();
|
|
|
|
|
self.proj_to_id.shrink_to_fit();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-14 17:20:11 +00:00
|
|
|
|
pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
self.proj_to_id.get(projection).copied()
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-14 17:20:11 +00:00
|
|
|
|
pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
let new_id = ProjectionId(self.proj_to_id.len() as u32);
|
|
|
|
|
match self.proj_to_id.entry(projection) {
|
|
|
|
|
Entry::Occupied(id) => *id.get(),
|
|
|
|
|
Entry::Vacant(e) => {
|
|
|
|
|
let key_clone = e.key().clone();
|
|
|
|
|
e.insert(new_id);
|
|
|
|
|
self.id_to_proj.insert(new_id, key_clone);
|
|
|
|
|
new_id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ProjectionId {
|
2023-10-14 17:20:11 +00:00
|
|
|
|
pub const EMPTY: ProjectionId = ProjectionId(0);
|
2023-09-08 09:09:41 +00:00
|
|
|
|
|
2023-12-01 12:46:46 +00:00
|
|
|
|
pub fn is_empty(self) -> bool {
|
|
|
|
|
self == ProjectionId::EMPTY
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-14 17:20:11 +00:00
|
|
|
|
pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
store.id_to_proj.get(&self).unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-14 17:20:11 +00:00
|
|
|
|
pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
let mut current = self.lookup(store).to_vec();
|
|
|
|
|
current.push(projection);
|
|
|
|
|
store.intern(current.into())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-24 17:59:15 +00:00
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
2023-03-13 08:42:24 +00:00
|
|
|
|
pub struct Place {
|
|
|
|
|
pub local: LocalId,
|
2023-09-08 09:09:41 +00:00
|
|
|
|
pub projection: ProjectionId,
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 08:42:24 +00:00
|
|
|
|
impl Place {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool {
|
|
|
|
|
self.local == child.local
|
2024-01-18 12:59:49 +00:00
|
|
|
|
&& child.projection.lookup(store).starts_with(self.projection.lookup(store))
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-07 09:03:15 +00:00
|
|
|
|
/// The place itself is not included
|
2023-09-08 09:09:41 +00:00
|
|
|
|
fn iterate_over_parents<'a>(
|
|
|
|
|
&'a self,
|
|
|
|
|
store: &'a ProjectionStore,
|
|
|
|
|
) -> impl Iterator<Item = Place> + 'a {
|
|
|
|
|
let projection = self.projection.lookup(store);
|
|
|
|
|
(0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| {
|
|
|
|
|
Some(Place { local: self.local, projection: store.intern_if_exist(x)? })
|
|
|
|
|
})
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 09:09:41 +00:00
|
|
|
|
fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place {
|
|
|
|
|
Place { local: self.local, projection: self.projection.project(projection, store) }
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<LocalId> for Place {
|
|
|
|
|
fn from(local: LocalId) -> Self {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
Self { local, projection: ProjectionId::EMPTY }
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum AggregateKind {
|
|
|
|
|
/// The type is of the element
|
|
|
|
|
Array(Ty),
|
|
|
|
|
/// The type is of the tuple
|
|
|
|
|
Tuple(Ty),
|
|
|
|
|
Adt(VariantId, Substitution),
|
|
|
|
|
Union(UnionId, FieldId),
|
|
|
|
|
Closure(Ty),
|
2023-12-25 22:12:45 +00:00
|
|
|
|
//Coroutine(LocalDefId, SubstsRef, Movability),
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
|
|
|
|
pub struct SwitchTargets {
|
|
|
|
|
/// Possible values. The locations to branch to in each case
|
|
|
|
|
/// are found in the corresponding indices from the `targets` vector.
|
|
|
|
|
values: SmallVec<[u128; 1]>,
|
|
|
|
|
|
|
|
|
|
/// Possible branch sites. The last element of this vector is used
|
|
|
|
|
/// for the otherwise branch, so targets.len() == values.len() + 1
|
|
|
|
|
/// should hold.
|
|
|
|
|
//
|
|
|
|
|
// This invariant is quite non-obvious and also could be improved.
|
|
|
|
|
// One way to make this invariant is to have something like this instead:
|
|
|
|
|
//
|
|
|
|
|
// branches: Vec<(ConstInt, BasicBlock)>,
|
|
|
|
|
// otherwise: Option<BasicBlock> // exhaustive if None
|
|
|
|
|
//
|
|
|
|
|
// However we’ve decided to keep this as-is until we figure a case
|
|
|
|
|
// where some other approach seems to be strictly better than other.
|
|
|
|
|
targets: SmallVec<[BasicBlockId; 2]>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SwitchTargets {
|
|
|
|
|
/// Creates switch targets from an iterator of values and target blocks.
|
|
|
|
|
///
|
|
|
|
|
/// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
|
|
|
|
|
/// `goto otherwise;`.
|
|
|
|
|
pub fn new(
|
|
|
|
|
targets: impl Iterator<Item = (u128, BasicBlockId)>,
|
|
|
|
|
otherwise: BasicBlockId,
|
|
|
|
|
) -> Self {
|
|
|
|
|
let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
|
|
|
|
|
targets.push(otherwise);
|
|
|
|
|
Self { values, targets }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
|
|
|
|
|
/// and to `else_` if not.
|
|
|
|
|
pub fn static_if(value: u128, then: BasicBlockId, else_: BasicBlockId) -> Self {
|
|
|
|
|
Self { values: smallvec![value], targets: smallvec![then, else_] }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the fallback target that is jumped to when none of the values match the operand.
|
|
|
|
|
pub fn otherwise(&self) -> BasicBlockId {
|
|
|
|
|
*self.targets.last().unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns an iterator over the switch targets.
|
|
|
|
|
///
|
|
|
|
|
/// The iterator will yield tuples containing the value and corresponding target to jump to, not
|
|
|
|
|
/// including the `otherwise` fallback target.
|
|
|
|
|
///
|
|
|
|
|
/// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
|
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = (u128, BasicBlockId)> + '_ {
|
|
|
|
|
iter::zip(&self.values, &self.targets).map(|(x, y)| (*x, *y))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns a slice with all possible jump targets (including the fallback target).
|
|
|
|
|
pub fn all_targets(&self) -> &[BasicBlockId] {
|
|
|
|
|
&self.targets
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
|
|
|
|
|
/// specific value. This cannot fail, as it'll return the `otherwise`
|
|
|
|
|
/// branch if there's not a specific match for the value.
|
|
|
|
|
pub fn target_for_value(&self, value: u128) -> BasicBlockId {
|
|
|
|
|
self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub struct Terminator {
|
2023-07-17 13:49:15 +00:00
|
|
|
|
pub span: MirSpan,
|
|
|
|
|
pub kind: TerminatorKind,
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 08:42:24 +00:00
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum TerminatorKind {
|
|
|
|
|
/// Block has one successor; we continue execution there.
|
|
|
|
|
Goto { target: BasicBlockId },
|
|
|
|
|
|
|
|
|
|
/// Switches based on the computed value.
|
|
|
|
|
///
|
|
|
|
|
/// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
|
|
|
|
|
/// integer, char, or bool, and must match the given type. Then, if the list of switch targets
|
|
|
|
|
/// contains the computed value, continues execution at the associated basic block. Otherwise,
|
|
|
|
|
/// continues execution at the "otherwise" basic block.
|
|
|
|
|
///
|
|
|
|
|
/// Target values may not appear more than once.
|
|
|
|
|
SwitchInt {
|
|
|
|
|
/// The discriminant value being tested.
|
|
|
|
|
discr: Operand,
|
|
|
|
|
|
|
|
|
|
targets: SwitchTargets,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Indicates that the landing pad is finished and that the process should continue unwinding.
|
|
|
|
|
///
|
|
|
|
|
/// Like a return, this marks the end of this invocation of the function.
|
|
|
|
|
///
|
|
|
|
|
/// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
|
|
|
|
|
/// deaggregation runs.
|
2023-08-20 16:31:22 +00:00
|
|
|
|
UnwindResume,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
|
|
|
|
|
/// Indicates that the landing pad is finished and that the process should abort.
|
|
|
|
|
///
|
|
|
|
|
/// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
|
|
|
|
|
/// cleanup blocks.
|
|
|
|
|
Abort,
|
|
|
|
|
|
|
|
|
|
/// Returns from the function.
|
|
|
|
|
///
|
|
|
|
|
/// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
|
|
|
|
|
/// likely at least assigns the value currently in the return place (`_0`) to the place
|
|
|
|
|
/// specified in the associated `Call` terminator in the calling function, as if assigned via
|
|
|
|
|
/// `dest = move _0`. It might additionally do other things, like have side-effects in the
|
|
|
|
|
/// aliasing model.
|
|
|
|
|
///
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// If the body is a coroutine body, this has slightly different semantics; it instead causes a
|
|
|
|
|
/// `CoroutineState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
|
2023-03-13 08:42:24 +00:00
|
|
|
|
/// to the return place.
|
|
|
|
|
Return,
|
|
|
|
|
|
|
|
|
|
/// Indicates a terminator that can never be reached.
|
|
|
|
|
///
|
|
|
|
|
/// Executing this terminator is UB.
|
|
|
|
|
Unreachable,
|
|
|
|
|
|
|
|
|
|
/// The behavior of this statement differs significantly before and after drop elaboration.
|
|
|
|
|
/// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
|
|
|
|
|
/// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
|
|
|
|
|
/// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
|
|
|
|
|
/// issue tracking if drop glue has any interesting semantics in addition to those of a function
|
|
|
|
|
/// call?)
|
|
|
|
|
///
|
|
|
|
|
/// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
|
|
|
|
|
/// `Drop` will be executed if...
|
|
|
|
|
///
|
|
|
|
|
/// **Needs clarification**: End of that sentence. This in effect should document the exact
|
|
|
|
|
/// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
|
|
|
|
|
///
|
|
|
|
|
/// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
|
|
|
|
|
/// > the place or one of its "parents" occurred more recently than a move out of it. This does not
|
|
|
|
|
/// > consider indirect assignments.
|
|
|
|
|
Drop { place: Place, target: BasicBlockId, unwind: Option<BasicBlockId> },
|
|
|
|
|
|
|
|
|
|
/// Drops the place and assigns a new value to it.
|
|
|
|
|
///
|
|
|
|
|
/// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
|
|
|
|
|
/// it then additionally assigns the `value` to the `place` as if by an assignment statement.
|
|
|
|
|
/// This assignment occurs both in the unwind and the regular code paths. The semantics are best
|
|
|
|
|
/// explained by the elaboration:
|
|
|
|
|
///
|
|
|
|
|
/// ```ignore (MIR)
|
|
|
|
|
/// BB0 {
|
|
|
|
|
/// DropAndReplace(P <- V, goto BB1, unwind BB2)
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// becomes
|
|
|
|
|
///
|
|
|
|
|
/// ```ignore (MIR)
|
|
|
|
|
/// BB0 {
|
|
|
|
|
/// Drop(P, goto BB1, unwind BB2)
|
|
|
|
|
/// }
|
|
|
|
|
/// BB1 {
|
|
|
|
|
/// // P is now uninitialized
|
|
|
|
|
/// P <- V
|
|
|
|
|
/// }
|
|
|
|
|
/// BB2 {
|
|
|
|
|
/// // P is now uninitialized -- its dtor panicked
|
|
|
|
|
/// P <- V
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// Disallowed after drop elaboration.
|
|
|
|
|
DropAndReplace {
|
|
|
|
|
place: Place,
|
|
|
|
|
value: Operand,
|
|
|
|
|
target: BasicBlockId,
|
|
|
|
|
unwind: Option<BasicBlockId>,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
|
|
|
|
|
/// the referred to function. The operand types must match the argument types of the function.
|
|
|
|
|
/// The return place type must match the return type. The type of the `func` operand must be
|
|
|
|
|
/// callable, meaning either a function pointer, a function type, or a closure type.
|
|
|
|
|
///
|
|
|
|
|
/// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
|
|
|
|
|
/// operands not aliasing the return place. It is unclear how this is justified in MIR, see
|
|
|
|
|
/// [#71117].
|
|
|
|
|
///
|
|
|
|
|
/// [#71117]: https://github.com/rust-lang/rust/issues/71117
|
|
|
|
|
Call {
|
|
|
|
|
/// The function that’s being called.
|
|
|
|
|
func: Operand,
|
|
|
|
|
/// Arguments the function is called with.
|
|
|
|
|
/// These are owned by the callee, which is free to modify them.
|
|
|
|
|
/// This allows the memory occupied by "by-value" arguments to be
|
|
|
|
|
/// reused across function calls without duplicating the contents.
|
|
|
|
|
args: Box<[Operand]>,
|
|
|
|
|
/// Where the returned value will be written
|
|
|
|
|
destination: Place,
|
|
|
|
|
/// Where to go after this call returns. If none, the call necessarily diverges.
|
|
|
|
|
target: Option<BasicBlockId>,
|
|
|
|
|
/// Cleanups to be done if the call unwinds.
|
|
|
|
|
cleanup: Option<BasicBlockId>,
|
|
|
|
|
/// `true` if this is from a call in HIR rather than from an overloaded
|
|
|
|
|
/// operator. True for overloaded function call.
|
|
|
|
|
from_hir_call: bool,
|
|
|
|
|
// This `Span` is the span of the function, without the dot and receiver
|
|
|
|
|
// (e.g. `foo(a, b)` in `x.foo(a, b)`
|
|
|
|
|
//fn_span: Span,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
|
|
|
|
|
/// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
|
|
|
|
|
/// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
|
|
|
|
|
/// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
|
|
|
|
|
/// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
|
|
|
|
|
/// assertion does not fail, execution continues at the specified basic block.
|
|
|
|
|
Assert {
|
|
|
|
|
cond: Operand,
|
|
|
|
|
expected: bool,
|
|
|
|
|
//msg: AssertMessage,
|
|
|
|
|
target: BasicBlockId,
|
|
|
|
|
cleanup: Option<BasicBlockId>,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Marks a suspend point.
|
|
|
|
|
///
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// Like `Return` terminators in coroutine bodies, this computes `value` and then a
|
|
|
|
|
/// `CoroutineState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
|
2023-03-13 08:42:24 +00:00
|
|
|
|
/// the return place of the function calling this one, and execution continues in the calling
|
|
|
|
|
/// function. When next invoked with the same first argument, execution of this function
|
|
|
|
|
/// continues at the `resume` basic block, with the second argument written to the `resume_arg`
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// place. If the coroutine is dropped before then, the `drop` basic block is invoked.
|
2023-03-13 08:42:24 +00:00
|
|
|
|
///
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// Not permitted in bodies that are not coroutine bodies, or after coroutine lowering.
|
2023-03-13 08:42:24 +00:00
|
|
|
|
///
|
|
|
|
|
/// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
|
|
|
|
|
Yield {
|
|
|
|
|
/// The value to return.
|
|
|
|
|
value: Operand,
|
|
|
|
|
/// Where to resume to.
|
|
|
|
|
resume: BasicBlockId,
|
|
|
|
|
/// The place to store the resume argument in.
|
|
|
|
|
resume_arg: Place,
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// Cleanup to be done if the coroutine is dropped at this suspend point.
|
2023-03-13 08:42:24 +00:00
|
|
|
|
drop: Option<BasicBlockId>,
|
|
|
|
|
},
|
|
|
|
|
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// Indicates the end of dropping a coroutine.
|
2023-03-13 08:42:24 +00:00
|
|
|
|
///
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// Semantically just a `return` (from the coroutines drop glue). Only permitted in the same situations
|
2023-03-13 08:42:24 +00:00
|
|
|
|
/// as `yield`.
|
|
|
|
|
///
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// **Needs clarification**: Is that even correct? The coroutine drop code is always confusing
|
2023-03-13 08:42:24 +00:00
|
|
|
|
/// to me, because it's not even really in the current body.
|
|
|
|
|
///
|
|
|
|
|
/// **Needs clarification**: Are there type system constraints on these terminators? Should
|
|
|
|
|
/// there be a "block type" like `cleanup` blocks for them?
|
2023-12-25 22:12:45 +00:00
|
|
|
|
CoroutineDrop,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
|
|
|
|
|
/// A block where control flow only ever takes one real path, but borrowck needs to be more
|
|
|
|
|
/// conservative.
|
|
|
|
|
///
|
|
|
|
|
/// At runtime this is semantically just a goto.
|
|
|
|
|
///
|
|
|
|
|
/// Disallowed after drop elaboration.
|
|
|
|
|
FalseEdge {
|
|
|
|
|
/// The target normal control flow will take.
|
|
|
|
|
real_target: BasicBlockId,
|
|
|
|
|
/// A block control flow could conceptually jump to, but won't in
|
|
|
|
|
/// practice.
|
|
|
|
|
imaginary_target: BasicBlockId,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// A terminator for blocks that only take one path in reality, but where we reserve the right
|
|
|
|
|
/// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
|
|
|
|
|
/// with no function calls for example.
|
|
|
|
|
///
|
|
|
|
|
/// At runtime this is semantically just a goto.
|
|
|
|
|
///
|
|
|
|
|
/// Disallowed after drop elaboration.
|
|
|
|
|
FalseUnwind {
|
|
|
|
|
/// The target normal control flow will take.
|
|
|
|
|
real_target: BasicBlockId,
|
|
|
|
|
/// The imaginary cleanup block link. This particular path will never be taken
|
|
|
|
|
/// in practice, but in order to avoid fragility we want to always
|
|
|
|
|
/// consider it in borrowck. We don't want to accept programs which
|
|
|
|
|
/// pass borrowck only when `panic=abort` or some assertions are disabled
|
|
|
|
|
/// due to release vs. debug mode builds. This needs to be an `Option` because
|
|
|
|
|
/// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
|
|
|
|
|
unwind: Option<BasicBlockId>,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-24 19:35:49 +00:00
|
|
|
|
// Order of variants in this enum matter: they are used to compare borrow kinds.
|
2023-03-13 08:42:24 +00:00
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
|
|
|
|
pub enum BorrowKind {
|
|
|
|
|
/// Data must be immutable and is aliasable.
|
|
|
|
|
Shared,
|
|
|
|
|
|
|
|
|
|
/// The immediately borrowed place must be immutable, but projections from
|
|
|
|
|
/// it don't need to be. For example, a shallow borrow of `a.b` doesn't
|
|
|
|
|
/// conflict with a mutable borrow of `a.b.c`.
|
|
|
|
|
///
|
|
|
|
|
/// This is used when lowering matches: when matching on a place we want to
|
|
|
|
|
/// ensure that place have the same value from the start of the match until
|
|
|
|
|
/// an arm is selected. This prevents this code from compiling:
|
|
|
|
|
/// ```compile_fail,E0510
|
|
|
|
|
/// let mut x = &Some(0);
|
|
|
|
|
/// match *x {
|
|
|
|
|
/// None => (),
|
|
|
|
|
/// Some(_) if { x = &None; false } => (),
|
|
|
|
|
/// Some(_) => (),
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
/// This can't be a shared borrow because mutably borrowing (*x as Some).0
|
|
|
|
|
/// should not prevent `if let None = x { ... }`, for example, because the
|
|
|
|
|
/// mutating `(*x as Some).0` can't affect the discriminant of `x`.
|
|
|
|
|
/// We can also report errors with this kind of borrow differently.
|
|
|
|
|
Shallow,
|
|
|
|
|
|
|
|
|
|
/// Data is mutable and not aliasable.
|
2024-02-25 16:57:03 +00:00
|
|
|
|
Mut { kind: MutBorrowKind },
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-24 19:35:49 +00:00
|
|
|
|
// Order of variants in this enum matter: they are used to compare borrow kinds.
|
2024-02-25 16:57:03 +00:00
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
|
|
|
|
pub enum MutBorrowKind {
|
2024-08-24 19:35:49 +00:00
|
|
|
|
/// Data must be immutable but not aliasable. This kind of borrow cannot currently
|
|
|
|
|
/// be expressed by the user and is used only in implicit closure bindings.
|
|
|
|
|
ClosureCapture,
|
2024-02-25 16:57:03 +00:00
|
|
|
|
Default,
|
|
|
|
|
/// This borrow arose from method-call auto-ref
|
|
|
|
|
/// (i.e., adjustment::Adjust::Borrow).
|
|
|
|
|
TwoPhasedBorrow,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BorrowKind {
|
|
|
|
|
fn from_hir(m: hir_def::type_ref::Mutability) -> Self {
|
|
|
|
|
match m {
|
|
|
|
|
hir_def::type_ref::Mutability::Shared => BorrowKind::Shared,
|
2024-02-25 16:57:03 +00:00
|
|
|
|
hir_def::type_ref::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn from_chalk(m: Mutability) -> Self {
|
|
|
|
|
match m {
|
|
|
|
|
Mutability::Not => BorrowKind::Shared,
|
2024-02-25 16:57:03 +00:00
|
|
|
|
Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
|
pub enum UnOp {
|
|
|
|
|
/// The `!` operator for logical inversion
|
|
|
|
|
Not,
|
|
|
|
|
/// The `-` operator for negation
|
|
|
|
|
Neg,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum BinOp {
|
|
|
|
|
/// The `+` operator (addition)
|
|
|
|
|
Add,
|
|
|
|
|
/// The `-` operator (subtraction)
|
|
|
|
|
Sub,
|
|
|
|
|
/// The `*` operator (multiplication)
|
|
|
|
|
Mul,
|
|
|
|
|
/// The `/` operator (division)
|
|
|
|
|
///
|
|
|
|
|
/// Division by zero is UB, because the compiler should have inserted checks
|
|
|
|
|
/// prior to this.
|
|
|
|
|
Div,
|
|
|
|
|
/// The `%` operator (modulus)
|
|
|
|
|
///
|
|
|
|
|
/// Using zero as the modulus (second operand) is UB, because the compiler
|
|
|
|
|
/// should have inserted checks prior to this.
|
|
|
|
|
Rem,
|
|
|
|
|
/// The `^` operator (bitwise xor)
|
|
|
|
|
BitXor,
|
|
|
|
|
/// The `&` operator (bitwise and)
|
|
|
|
|
BitAnd,
|
|
|
|
|
/// The `|` operator (bitwise or)
|
|
|
|
|
BitOr,
|
|
|
|
|
/// The `<<` operator (shift left)
|
|
|
|
|
///
|
|
|
|
|
/// The offset is truncated to the size of the first operand before shifting.
|
|
|
|
|
Shl,
|
|
|
|
|
/// The `>>` operator (shift right)
|
|
|
|
|
///
|
|
|
|
|
/// The offset is truncated to the size of the first operand before shifting.
|
|
|
|
|
Shr,
|
|
|
|
|
/// The `==` operator (equality)
|
|
|
|
|
Eq,
|
|
|
|
|
/// The `<` operator (less than)
|
|
|
|
|
Lt,
|
|
|
|
|
/// The `<=` operator (less than or equal to)
|
|
|
|
|
Le,
|
|
|
|
|
/// The `!=` operator (not equal to)
|
|
|
|
|
Ne,
|
|
|
|
|
/// The `>=` operator (greater than or equal to)
|
|
|
|
|
Ge,
|
|
|
|
|
/// The `>` operator (greater than)
|
|
|
|
|
Gt,
|
|
|
|
|
/// The `ptr.offset` operator
|
|
|
|
|
Offset,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BinOp {
|
|
|
|
|
fn run_compare<T: PartialEq + PartialOrd>(&self, l: T, r: T) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
BinOp::Ge => l >= r,
|
|
|
|
|
BinOp::Gt => l > r,
|
|
|
|
|
BinOp::Le => l <= r,
|
|
|
|
|
BinOp::Lt => l < r,
|
|
|
|
|
BinOp::Eq => l == r,
|
|
|
|
|
BinOp::Ne => l != r,
|
|
|
|
|
x => panic!("`run_compare` called on operator {x:?}"),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 08:42:24 +00:00
|
|
|
|
impl Display for BinOp {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
f.write_str(match self {
|
|
|
|
|
BinOp::Add => "+",
|
|
|
|
|
BinOp::Sub => "-",
|
|
|
|
|
BinOp::Mul => "*",
|
|
|
|
|
BinOp::Div => "/",
|
|
|
|
|
BinOp::Rem => "%",
|
|
|
|
|
BinOp::BitXor => "^",
|
|
|
|
|
BinOp::BitAnd => "&",
|
|
|
|
|
BinOp::BitOr => "|",
|
|
|
|
|
BinOp::Shl => "<<",
|
|
|
|
|
BinOp::Shr => ">>",
|
|
|
|
|
BinOp::Eq => "==",
|
|
|
|
|
BinOp::Lt => "<",
|
|
|
|
|
BinOp::Le => "<=",
|
|
|
|
|
BinOp::Ne => "!=",
|
|
|
|
|
BinOp::Ge => ">=",
|
|
|
|
|
BinOp::Gt => ">",
|
|
|
|
|
BinOp::Offset => "`offset`",
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<hir_def::hir::ArithOp> for BinOp {
|
|
|
|
|
fn from(value: hir_def::hir::ArithOp) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
hir_def::hir::ArithOp::Add => BinOp::Add,
|
|
|
|
|
hir_def::hir::ArithOp::Mul => BinOp::Mul,
|
|
|
|
|
hir_def::hir::ArithOp::Sub => BinOp::Sub,
|
|
|
|
|
hir_def::hir::ArithOp::Div => BinOp::Div,
|
|
|
|
|
hir_def::hir::ArithOp::Rem => BinOp::Rem,
|
|
|
|
|
hir_def::hir::ArithOp::Shl => BinOp::Shl,
|
|
|
|
|
hir_def::hir::ArithOp::Shr => BinOp::Shr,
|
|
|
|
|
hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
|
|
|
|
|
hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
|
|
|
|
|
hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<hir_def::hir::CmpOp> for BinOp {
|
|
|
|
|
fn from(value: hir_def::hir::CmpOp) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
|
|
|
|
|
hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
|
|
|
|
|
hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
|
|
|
|
|
hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
|
|
|
|
|
hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
|
|
|
|
|
hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Operand> for Rvalue {
|
|
|
|
|
fn from(x: Operand) -> Self {
|
|
|
|
|
Self::Use(x)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum CastKind {
|
|
|
|
|
/// An exposing pointer to address cast. A cast between a pointer and an integer type, or
|
|
|
|
|
/// between a function pointer and an integer type.
|
|
|
|
|
/// See the docs on `expose_addr` for more details.
|
|
|
|
|
PointerExposeAddress,
|
|
|
|
|
/// An address-to-pointer cast that picks up an exposed provenance.
|
|
|
|
|
/// See the docs on `from_exposed_addr` for more details.
|
|
|
|
|
PointerFromExposedAddress,
|
|
|
|
|
/// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are
|
|
|
|
|
/// translated into `&raw mut/const *r`, i.e., they are not actually casts.
|
2024-08-28 15:12:16 +00:00
|
|
|
|
PtrToPtr,
|
|
|
|
|
/// Pointer related casts that are done by coercions.
|
|
|
|
|
PointerCoercion(PointerCast),
|
2023-03-13 08:42:24 +00:00
|
|
|
|
/// Cast into a dyn* object.
|
|
|
|
|
DynStar,
|
|
|
|
|
IntToInt,
|
|
|
|
|
FloatToInt,
|
|
|
|
|
FloatToFloat,
|
|
|
|
|
IntToFloat,
|
|
|
|
|
FnPtrToPtr,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum Rvalue {
|
|
|
|
|
/// Yields the operand unchanged
|
|
|
|
|
Use(Operand),
|
|
|
|
|
|
|
|
|
|
/// Creates an array where each element is the value of the operand.
|
|
|
|
|
///
|
|
|
|
|
/// Corresponds to source code like `[x; 32]`.
|
|
|
|
|
Repeat(Operand, Const),
|
|
|
|
|
|
|
|
|
|
/// Creates a reference of the indicated kind to the place.
|
|
|
|
|
///
|
|
|
|
|
/// There is not much to document here, because besides the obvious parts the semantics of this
|
|
|
|
|
/// are essentially entirely a part of the aliasing model. There are many UCG issues discussing
|
|
|
|
|
/// exactly what the behavior of this operation should be.
|
|
|
|
|
///
|
|
|
|
|
/// `Shallow` borrows are disallowed after drop lowering.
|
|
|
|
|
Ref(BorrowKind, Place),
|
|
|
|
|
|
|
|
|
|
/// Creates a pointer/reference to the given thread local.
|
|
|
|
|
///
|
|
|
|
|
/// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a
|
|
|
|
|
/// `*const T`, and if neither of those apply a `&T`.
|
|
|
|
|
///
|
|
|
|
|
/// **Note:** This is a runtime operation that actually executes code and is in this sense more
|
|
|
|
|
/// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to
|
|
|
|
|
/// SIGILL for some reason that I (JakobDegen) never got a chance to look into.
|
|
|
|
|
///
|
|
|
|
|
/// **Needs clarification**: Are there weird additional semantics here related to the runtime
|
|
|
|
|
/// nature of this operation?
|
|
|
|
|
//ThreadLocalRef(DefId),
|
|
|
|
|
|
|
|
|
|
/// Creates a pointer with the indicated mutability to the place.
|
|
|
|
|
///
|
|
|
|
|
/// This is generated by pointer casts like `&v as *const _` or raw address of expressions like
|
|
|
|
|
/// `&raw v` or `addr_of!(v)`.
|
|
|
|
|
///
|
|
|
|
|
/// Like with references, the semantics of this operation are heavily dependent on the aliasing
|
|
|
|
|
/// model.
|
|
|
|
|
//AddressOf(Mutability, Place),
|
|
|
|
|
|
|
|
|
|
/// Yields the length of the place, as a `usize`.
|
|
|
|
|
///
|
|
|
|
|
/// If the type of the place is an array, this is the array length. For slices (`[T]`, not
|
|
|
|
|
/// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
|
|
|
|
|
/// ill-formed for places of other types.
|
|
|
|
|
Len(Place),
|
|
|
|
|
|
|
|
|
|
/// Performs essentially all of the casts that can be performed via `as`.
|
|
|
|
|
///
|
|
|
|
|
/// This allows for casts from/to a variety of types.
|
|
|
|
|
///
|
|
|
|
|
/// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why
|
|
|
|
|
/// `ArrayToPointer` and `MutToConstPointer` are special.
|
|
|
|
|
Cast(CastKind, Operand, Ty),
|
|
|
|
|
|
|
|
|
|
// FIXME link to `pointer::offset` when it hits stable.
|
2024-06-09 10:54:50 +00:00
|
|
|
|
// /// * `Offset` has the same semantics as `pointer::offset`, except that the second
|
|
|
|
|
// /// parameter may be a `usize` as well.
|
|
|
|
|
// /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
|
|
|
|
|
// /// raw pointers, or function pointers and return a `bool`. The types of the operands must be
|
|
|
|
|
// /// matching, up to the usual caveat of the lifetimes in function pointers.
|
|
|
|
|
// /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
|
|
|
|
|
// /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is
|
|
|
|
|
// /// truncated as needed.
|
|
|
|
|
// /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
|
|
|
|
|
// /// types and return a value of that type.
|
|
|
|
|
// /// * The remaining operations accept signed integers, unsigned integers, or floats with
|
|
|
|
|
// /// matching types and return a value of that type.
|
2023-03-13 08:42:24 +00:00
|
|
|
|
//BinaryOp(BinOp, Box<(Operand, Operand)>),
|
|
|
|
|
/// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
|
|
|
|
|
///
|
|
|
|
|
/// When overflow checking is disabled and we are generating run-time code, the error condition
|
|
|
|
|
/// is false. Otherwise, and always during CTFE, the error condition is determined as described
|
|
|
|
|
/// below.
|
|
|
|
|
///
|
|
|
|
|
/// For addition, subtraction, and multiplication on integers the error condition is set when
|
|
|
|
|
/// the infinite precision result would be unequal to the actual result.
|
|
|
|
|
///
|
|
|
|
|
/// For shift operations on integers the error condition is set when the value of right-hand
|
|
|
|
|
/// side is greater than or equal to the number of bits in the type of the left-hand side, or
|
|
|
|
|
/// when the value of right-hand side is negative.
|
|
|
|
|
///
|
|
|
|
|
/// Other combinations of types and operators are unsupported.
|
|
|
|
|
CheckedBinaryOp(BinOp, Operand, Operand),
|
|
|
|
|
|
|
|
|
|
/// Computes a value as described by the operation.
|
|
|
|
|
//NullaryOp(NullOp, Ty),
|
|
|
|
|
|
|
|
|
|
/// Exactly like `BinaryOp`, but less operands.
|
|
|
|
|
///
|
|
|
|
|
/// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
|
|
|
|
|
/// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds
|
|
|
|
|
/// return a value with the same type as their operand.
|
|
|
|
|
UnaryOp(UnOp, Operand),
|
|
|
|
|
|
|
|
|
|
/// Computes the discriminant of the place, returning it as an integer of type
|
|
|
|
|
/// [`discriminant_ty`]. Returns zero for types without discriminant.
|
|
|
|
|
///
|
|
|
|
|
/// The validity requirements for the underlying value are undecided for this rvalue, see
|
|
|
|
|
/// [#91095]. Note too that the value of the discriminant is not the same thing as the
|
|
|
|
|
/// variant index; use [`discriminant_for_variant`] to convert.
|
|
|
|
|
///
|
|
|
|
|
/// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty
|
|
|
|
|
/// [#91095]: https://github.com/rust-lang/rust/issues/91095
|
|
|
|
|
/// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant
|
|
|
|
|
Discriminant(Place),
|
|
|
|
|
|
|
|
|
|
/// Creates an aggregate value, like a tuple or struct.
|
|
|
|
|
///
|
|
|
|
|
/// This is needed because dataflow analysis needs to distinguish
|
|
|
|
|
/// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo`
|
|
|
|
|
/// has a destructor.
|
|
|
|
|
///
|
2023-12-25 22:12:45 +00:00
|
|
|
|
/// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After
|
|
|
|
|
/// coroutine lowering, `Coroutine` aggregate kinds are disallowed too.
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Aggregate(AggregateKind, Box<[Operand]>),
|
|
|
|
|
|
|
|
|
|
/// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
|
|
|
|
|
///
|
|
|
|
|
/// This is different from a normal transmute because dataflow analysis will treat the box as
|
|
|
|
|
/// initialized but its content as uninitialized. Like other pointer casts, this in general
|
|
|
|
|
/// affects alias analysis.
|
|
|
|
|
ShallowInitBox(Operand, Ty),
|
|
|
|
|
|
|
|
|
|
/// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer.
|
|
|
|
|
ShallowInitBoxWithAlloc(Ty),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
|
2023-03-13 08:42:24 +00:00
|
|
|
|
/// A CopyForDeref is equivalent to a read from a place at the
|
|
|
|
|
/// codegen level, but is treated specially by drop elaboration. When such a read happens, it
|
|
|
|
|
/// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
|
|
|
|
|
/// that the only use of the returned value is a deref operation, immediately
|
|
|
|
|
/// followed by one or more projections. Drop elaboration treats this rvalue as if the
|
|
|
|
|
/// read never happened and just projects further. This allows simplifying various MIR
|
|
|
|
|
/// optimizations and codegen backends that previously had to handle deref operations anywhere
|
|
|
|
|
/// in a place.
|
|
|
|
|
CopyForDeref(Place),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum StatementKind {
|
|
|
|
|
Assign(Place, Rvalue),
|
2023-09-24 17:59:15 +00:00
|
|
|
|
FakeRead(Place),
|
2023-03-13 08:42:24 +00:00
|
|
|
|
//SetDiscriminant {
|
|
|
|
|
// place: Box<Place>,
|
|
|
|
|
// variant_index: VariantIdx,
|
|
|
|
|
//},
|
|
|
|
|
Deinit(Place),
|
|
|
|
|
StorageLive(LocalId),
|
|
|
|
|
StorageDead(LocalId),
|
|
|
|
|
//Retag(RetagKind, Box<Place>),
|
|
|
|
|
//AscribeUserType(Place, UserTypeProjection, Variance),
|
|
|
|
|
//Intrinsic(Box<NonDivergingIntrinsic>),
|
|
|
|
|
Nop,
|
|
|
|
|
}
|
|
|
|
|
impl StatementKind {
|
|
|
|
|
fn with_span(self, span: MirSpan) -> Statement {
|
|
|
|
|
Statement { kind: self, span }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub struct Statement {
|
|
|
|
|
pub kind: StatementKind,
|
|
|
|
|
pub span: MirSpan,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct BasicBlock {
|
|
|
|
|
/// List of statements in this block.
|
|
|
|
|
pub statements: Vec<Statement>,
|
|
|
|
|
|
|
|
|
|
/// Terminator for this block.
|
|
|
|
|
///
|
|
|
|
|
/// N.B., this should generally ONLY be `None` during construction.
|
|
|
|
|
/// Therefore, you should generally access it via the
|
|
|
|
|
/// `terminator()` or `terminator_mut()` methods. The only
|
|
|
|
|
/// exception is that certain passes, such as `simplify_cfg`, swap
|
|
|
|
|
/// out the terminator temporarily with `None` while they continue
|
|
|
|
|
/// to recurse over the set of basic blocks.
|
|
|
|
|
pub terminator: Option<Terminator>,
|
|
|
|
|
|
|
|
|
|
/// If true, this block lies on an unwind path. This is used
|
|
|
|
|
/// during codegen where distinct kinds of basic blocks may be
|
|
|
|
|
/// generated (particularly for MSVC cleanup). Unwind blocks must
|
|
|
|
|
/// only branch to other unwind blocks.
|
|
|
|
|
pub is_cleanup: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct MirBody {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
pub projection_store: ProjectionStore,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
pub basic_blocks: Arena<BasicBlock>,
|
|
|
|
|
pub locals: Arena<Local>,
|
|
|
|
|
pub start_block: BasicBlockId,
|
|
|
|
|
pub owner: DefWithBodyId,
|
|
|
|
|
pub binding_locals: ArenaMap<BindingId, LocalId>,
|
|
|
|
|
pub param_locals: Vec<LocalId>,
|
|
|
|
|
/// This field stores the closures directly owned by this body. It is used
|
|
|
|
|
/// in traversing every mir body.
|
|
|
|
|
pub closures: Vec<ClosureId>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl MirBody {
|
2023-12-01 12:46:46 +00:00
|
|
|
|
pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
|
|
|
|
|
self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-08 09:09:41 +00:00
|
|
|
|
fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
|
|
|
|
|
fn for_operand(
|
|
|
|
|
op: &mut Operand,
|
|
|
|
|
f: &mut impl FnMut(&mut Place, &mut ProjectionStore),
|
|
|
|
|
store: &mut ProjectionStore,
|
|
|
|
|
) {
|
2023-06-05 09:04:23 +00:00
|
|
|
|
match op {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Operand::Copy(p) | Operand::Move(p) => {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
f(p, store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Operand::Constant(_) | Operand::Static(_) => (),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
for (_, block) in self.basic_blocks.iter_mut() {
|
|
|
|
|
for statement in &mut block.statements {
|
|
|
|
|
match &mut statement.kind {
|
|
|
|
|
StatementKind::Assign(p, r) => {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
f(p, &mut self.projection_store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
match r {
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Rvalue::ShallowInitBoxWithAlloc(_) => (),
|
|
|
|
|
Rvalue::ShallowInitBox(o, _)
|
|
|
|
|
| Rvalue::UnaryOp(_, o)
|
|
|
|
|
| Rvalue::Cast(_, o, _)
|
|
|
|
|
| Rvalue::Repeat(o, _)
|
2023-09-08 09:09:41 +00:00
|
|
|
|
| Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store),
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Rvalue::CopyForDeref(p)
|
|
|
|
|
| Rvalue::Discriminant(p)
|
|
|
|
|
| Rvalue::Len(p)
|
2023-09-08 09:09:41 +00:00
|
|
|
|
| Rvalue::Ref(_, p) => f(p, &mut self.projection_store),
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Rvalue::CheckedBinaryOp(_, o1, o2) => {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
for_operand(o1, &mut f, &mut self.projection_store);
|
|
|
|
|
for_operand(o2, &mut f, &mut self.projection_store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Rvalue::Aggregate(_, ops) => {
|
|
|
|
|
for op in ops.iter_mut() {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
for_operand(op, &mut f, &mut self.projection_store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-24 17:59:15 +00:00
|
|
|
|
StatementKind::FakeRead(p) | StatementKind::Deinit(p) => {
|
|
|
|
|
f(p, &mut self.projection_store)
|
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
StatementKind::StorageLive(_)
|
|
|
|
|
| StatementKind::StorageDead(_)
|
|
|
|
|
| StatementKind::Nop => (),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
match &mut block.terminator {
|
|
|
|
|
Some(x) => match &mut x.kind {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
TerminatorKind::SwitchInt { discr, .. } => {
|
|
|
|
|
for_operand(discr, &mut f, &mut self.projection_store)
|
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TerminatorKind::FalseEdge { .. }
|
|
|
|
|
| TerminatorKind::FalseUnwind { .. }
|
|
|
|
|
| TerminatorKind::Goto { .. }
|
2023-08-19 11:10:25 +00:00
|
|
|
|
| TerminatorKind::UnwindResume
|
2023-12-25 22:12:45 +00:00
|
|
|
|
| TerminatorKind::CoroutineDrop
|
2023-03-13 08:42:24 +00:00
|
|
|
|
| TerminatorKind::Abort
|
|
|
|
|
| TerminatorKind::Return
|
|
|
|
|
| TerminatorKind::Unreachable => (),
|
|
|
|
|
TerminatorKind::Drop { place, .. } => {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
f(place, &mut self.projection_store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TerminatorKind::DropAndReplace { place, value, .. } => {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
f(place, &mut self.projection_store);
|
|
|
|
|
for_operand(value, &mut f, &mut self.projection_store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TerminatorKind::Call { func, args, destination, .. } => {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
for_operand(func, &mut f, &mut self.projection_store);
|
|
|
|
|
args.iter_mut()
|
|
|
|
|
.for_each(|x| for_operand(x, &mut f, &mut self.projection_store));
|
|
|
|
|
f(destination, &mut self.projection_store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TerminatorKind::Assert { cond, .. } => {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
for_operand(cond, &mut f, &mut self.projection_store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
TerminatorKind::Yield { value, resume_arg, .. } => {
|
2023-09-08 09:09:41 +00:00
|
|
|
|
for_operand(value, &mut f, &mut self.projection_store);
|
|
|
|
|
f(resume_arg, &mut self.projection_store);
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2023-03-13 08:42:24 +00:00
|
|
|
|
None => (),
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 08:42:24 +00:00
|
|
|
|
fn shrink_to_fit(&mut self) {
|
|
|
|
|
let MirBody {
|
|
|
|
|
basic_blocks,
|
2023-06-05 09:04:23 +00:00
|
|
|
|
locals,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
start_block: _,
|
2023-06-05 09:04:23 +00:00
|
|
|
|
owner: _,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
binding_locals,
|
|
|
|
|
param_locals,
|
|
|
|
|
closures,
|
2023-09-08 09:09:41 +00:00
|
|
|
|
projection_store,
|
2023-06-05 09:04:23 +00:00
|
|
|
|
} = self;
|
2023-09-08 09:09:41 +00:00
|
|
|
|
projection_store.shrink_to_fit();
|
2023-03-13 08:42:24 +00:00
|
|
|
|
basic_blocks.shrink_to_fit();
|
2023-06-05 09:04:23 +00:00
|
|
|
|
locals.shrink_to_fit();
|
2023-03-13 08:42:24 +00:00
|
|
|
|
binding_locals.shrink_to_fit();
|
|
|
|
|
param_locals.shrink_to_fit();
|
|
|
|
|
closures.shrink_to_fit();
|
|
|
|
|
for (_, b) in basic_blocks.iter_mut() {
|
|
|
|
|
let BasicBlock { statements, terminator: _, is_cleanup: _ } = b;
|
|
|
|
|
statements.shrink_to_fit();
|
2023-06-05 09:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-13 08:42:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
|
|
|
pub enum MirSpan {
|
|
|
|
|
ExprId(ExprId),
|
|
|
|
|
PatId(PatId),
|
2024-07-22 15:12:55 +00:00
|
|
|
|
BindingId(BindingId),
|
2024-03-06 14:57:05 +00:00
|
|
|
|
SelfParam,
|
2023-03-13 08:42:24 +00:00
|
|
|
|
Unknown,
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-22 07:39:20 +00:00
|
|
|
|
impl MirSpan {
|
|
|
|
|
pub fn is_ref_span(&self, body: &Body) -> bool {
|
|
|
|
|
match *self {
|
|
|
|
|
MirSpan::ExprId(expr) => matches!(body[expr], Expr::Ref { .. }),
|
|
|
|
|
// FIXME: Figure out if this is correct wrt. match ergonomics.
|
|
|
|
|
MirSpan::BindingId(binding) => matches!(
|
|
|
|
|
body.bindings[binding].mode,
|
|
|
|
|
BindingAnnotation::Ref | BindingAnnotation::RefMut
|
|
|
|
|
),
|
|
|
|
|
MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 08:42:24 +00:00
|
|
|
|
impl_from!(ExprId, PatId for MirSpan);
|
2023-12-01 12:46:46 +00:00
|
|
|
|
|
|
|
|
|
impl From<&ExprId> for MirSpan {
|
|
|
|
|
fn from(value: &ExprId) -> Self {
|
|
|
|
|
(*value).into()
|
|
|
|
|
}
|
|
|
|
|
}
|