summaryrefslogtreecommitdiffstats
path: root/compiler/stable_mir/src/mir/body.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/stable_mir/src/mir/body.rs')
-rw-r--r--compiler/stable_mir/src/mir/body.rs622
1 files changed, 575 insertions, 47 deletions
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)
}
}