mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
organize mir pretty.rs and move more things into it; move statement-related things out of mir/mod.rs
This commit is contained in:
parent
57444cf9f3
commit
be8f5f6e7f
@ -37,7 +37,7 @@ use either::Either;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::{iter, mem};
|
||||
|
||||
@ -56,6 +56,7 @@ pub mod patch;
|
||||
pub mod pretty;
|
||||
mod query;
|
||||
pub mod spanview;
|
||||
mod statement;
|
||||
mod syntax;
|
||||
pub mod tcx;
|
||||
mod terminator;
|
||||
@ -71,6 +72,7 @@ pub use self::pretty::{
|
||||
};
|
||||
pub use consts::*;
|
||||
use pretty::pretty_print_const_value;
|
||||
pub use statement::*;
|
||||
pub use syntax::*;
|
||||
pub use terminator::*;
|
||||
|
||||
@ -1150,20 +1152,6 @@ pub struct VarDebugInfo<'tcx> {
|
||||
pub argument_index: Option<u16>,
|
||||
}
|
||||
|
||||
impl Debug for VarDebugInfo<'_> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
|
||||
pre_fmt_projection(&projection[..], fmt)?;
|
||||
write!(fmt, "({}: {})", self.name, ty)?;
|
||||
post_fmt_projection(&projection[..], fmt)?;
|
||||
} else {
|
||||
write!(fmt, "{}", self.name)?;
|
||||
}
|
||||
|
||||
write!(fmt, " => {:?}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// BasicBlock
|
||||
|
||||
@ -1320,576 +1308,6 @@ impl<'tcx> BasicBlockData<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<O> AssertKind<O> {
|
||||
/// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
|
||||
pub fn is_optional_overflow_check(&self) -> bool {
|
||||
use AssertKind::*;
|
||||
use BinOp::*;
|
||||
matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
|
||||
}
|
||||
|
||||
/// Get the message that is printed at runtime when this assertion fails.
|
||||
///
|
||||
/// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
|
||||
/// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
|
||||
/// instead of printing a static message.
|
||||
pub fn description(&self) -> &'static str {
|
||||
use AssertKind::*;
|
||||
match self {
|
||||
Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
|
||||
Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
|
||||
Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
|
||||
Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
|
||||
Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
|
||||
OverflowNeg(_) => "attempt to negate with overflow",
|
||||
Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
|
||||
Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
|
||||
Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
|
||||
DivisionByZero(_) => "attempt to divide by zero",
|
||||
RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
|
||||
ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
|
||||
ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
|
||||
ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
|
||||
ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
|
||||
BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
|
||||
bug!("Unexpected AssertKind")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
|
||||
///
|
||||
/// Needs to be kept in sync with the run-time behavior (which is defined by
|
||||
/// `AssertKind::description` and the lang items mentioned in its docs).
|
||||
/// Note that we deliberately show more details here than we do at runtime, such as the actual
|
||||
/// numbers that overflowed -- it is much easier to do so here than at runtime.
|
||||
pub fn fmt_assert_args<W: Write>(&self, f: &mut W) -> fmt::Result
|
||||
where
|
||||
O: Debug,
|
||||
{
|
||||
use AssertKind::*;
|
||||
match self {
|
||||
BoundsCheck { ref len, ref index } => write!(
|
||||
f,
|
||||
"\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
|
||||
),
|
||||
|
||||
OverflowNeg(op) => {
|
||||
write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
|
||||
}
|
||||
DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
|
||||
RemainderByZero(op) => write!(
|
||||
f,
|
||||
"\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
|
||||
),
|
||||
Overflow(BinOp::Add, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Sub, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Mul, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Div, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Rem, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Shr, _, r) => {
|
||||
write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
|
||||
}
|
||||
Overflow(BinOp::Shl, _, r) => {
|
||||
write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
|
||||
}
|
||||
MisalignedPointerDereference { required, found } => {
|
||||
write!(
|
||||
f,
|
||||
"\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
|
||||
)
|
||||
}
|
||||
_ => write!(f, "\"{}\"", self.description()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
|
||||
///
|
||||
/// Needs to be kept in sync with the run-time behavior (which is defined by
|
||||
/// `AssertKind::description` and the lang items mentioned in its docs).
|
||||
/// Note that we deliberately show more details here than we do at runtime, such as the actual
|
||||
/// numbers that overflowed -- it is much easier to do so here than at runtime.
|
||||
pub fn diagnostic_message(&self) -> DiagnosticMessage {
|
||||
use crate::fluent_generated::*;
|
||||
use AssertKind::*;
|
||||
|
||||
match self {
|
||||
BoundsCheck { .. } => middle_bounds_check,
|
||||
Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
|
||||
Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
|
||||
Overflow(_, _, _) => middle_assert_op_overflow,
|
||||
OverflowNeg(_) => middle_assert_overflow_neg,
|
||||
DivisionByZero(_) => middle_assert_divide_by_zero,
|
||||
RemainderByZero(_) => middle_assert_remainder_by_zero,
|
||||
ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
|
||||
ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
|
||||
ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
|
||||
ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
|
||||
|
||||
MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
|
||||
where
|
||||
O: fmt::Debug,
|
||||
{
|
||||
use AssertKind::*;
|
||||
|
||||
macro_rules! add {
|
||||
($name: expr, $value: expr) => {
|
||||
adder($name.into(), $value.into_diagnostic_arg());
|
||||
};
|
||||
}
|
||||
|
||||
match self {
|
||||
BoundsCheck { len, index } => {
|
||||
add!("len", format!("{len:?}"));
|
||||
add!("index", format!("{index:?}"));
|
||||
}
|
||||
Overflow(BinOp::Shl | BinOp::Shr, _, val)
|
||||
| DivisionByZero(val)
|
||||
| RemainderByZero(val)
|
||||
| OverflowNeg(val) => {
|
||||
add!("val", format!("{val:#?}"));
|
||||
}
|
||||
Overflow(binop, left, right) => {
|
||||
add!("op", binop.to_hir_binop().as_str());
|
||||
add!("left", format!("{left:#?}"));
|
||||
add!("right", format!("{right:#?}"));
|
||||
}
|
||||
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
|
||||
MisalignedPointerDereference { required, found } => {
|
||||
add!("required", format!("{required:#?}"));
|
||||
add!("found", format!("{found:#?}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Statements
|
||||
|
||||
/// A statement in a basic block, including information about its source code.
|
||||
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct Statement<'tcx> {
|
||||
pub source_info: SourceInfo,
|
||||
pub kind: StatementKind<'tcx>,
|
||||
}
|
||||
|
||||
impl Statement<'_> {
|
||||
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
|
||||
/// invalidating statement indices in `Location`s.
|
||||
pub fn make_nop(&mut self) {
|
||||
self.kind = StatementKind::Nop
|
||||
}
|
||||
|
||||
/// Changes a statement to a nop and returns the original statement.
|
||||
#[must_use = "If you don't need the statement, use `make_nop` instead"]
|
||||
pub fn replace_nop(&mut self) -> Self {
|
||||
Statement {
|
||||
source_info: self.source_info,
|
||||
kind: mem::replace(&mut self.kind, StatementKind::Nop),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Statement<'_> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
use self::StatementKind::*;
|
||||
match self.kind {
|
||||
Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
|
||||
FakeRead(box (ref cause, ref place)) => {
|
||||
write!(fmt, "FakeRead({cause:?}, {place:?})")
|
||||
}
|
||||
Retag(ref kind, ref place) => write!(
|
||||
fmt,
|
||||
"Retag({}{:?})",
|
||||
match kind {
|
||||
RetagKind::FnEntry => "[fn entry] ",
|
||||
RetagKind::TwoPhase => "[2phase] ",
|
||||
RetagKind::Raw => "[raw] ",
|
||||
RetagKind::Default => "",
|
||||
},
|
||||
place,
|
||||
),
|
||||
StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
|
||||
StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
|
||||
SetDiscriminant { ref place, variant_index } => {
|
||||
write!(fmt, "discriminant({place:?}) = {variant_index:?}")
|
||||
}
|
||||
Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
|
||||
PlaceMention(ref place) => {
|
||||
write!(fmt, "PlaceMention({place:?})")
|
||||
}
|
||||
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
|
||||
write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
|
||||
}
|
||||
Coverage(box self::Coverage { ref kind, code_region: Some(ref rgn) }) => {
|
||||
write!(fmt, "Coverage::{kind:?} for {rgn:?}")
|
||||
}
|
||||
Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
|
||||
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
|
||||
ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
|
||||
Nop => write!(fmt, "nop"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> StatementKind<'tcx> {
|
||||
pub fn as_assign_mut(&mut self) -> Option<&mut (Place<'tcx>, Rvalue<'tcx>)> {
|
||||
match self {
|
||||
StatementKind::Assign(x) => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_assign(&self) -> Option<&(Place<'tcx>, Rvalue<'tcx>)> {
|
||||
match self {
|
||||
StatementKind::Assign(x) => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Places
|
||||
|
||||
impl<V, T> ProjectionElem<V, T> {
|
||||
/// Returns `true` if the target of this projection may refer to a different region of memory
|
||||
/// than the base.
|
||||
fn is_indirect(&self) -> bool {
|
||||
match self {
|
||||
Self::Deref => true,
|
||||
|
||||
Self::Field(_, _)
|
||||
| Self::Index(_)
|
||||
| Self::OpaqueCast(_)
|
||||
| Self::ConstantIndex { .. }
|
||||
| Self::Subslice { .. }
|
||||
| Self::Downcast(_, _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the target of this projection always refers to the same memory region
|
||||
/// whatever the state of the program.
|
||||
pub fn is_stable_offset(&self) -> bool {
|
||||
match self {
|
||||
Self::Deref | Self::Index(_) => false,
|
||||
Self::Field(_, _)
|
||||
| Self::OpaqueCast(_)
|
||||
| Self::ConstantIndex { .. }
|
||||
| Self::Subslice { .. }
|
||||
| Self::Downcast(_, _) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
|
||||
pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
|
||||
matches!(*self, Self::Downcast(_, x) if x == v)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a `Field` projection with the given index.
|
||||
pub fn is_field_to(&self, f: FieldIdx) -> bool {
|
||||
matches!(*self, Self::Field(x, _) if x == f)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is accepted inside `VarDebugInfoContents::Place`.
|
||||
pub fn can_use_in_debuginfo(&self) -> bool {
|
||||
match self {
|
||||
Self::ConstantIndex { from_end: false, .. }
|
||||
| Self::Deref
|
||||
| Self::Downcast(_, _)
|
||||
| Self::Field(_, _) => true,
|
||||
Self::ConstantIndex { from_end: true, .. }
|
||||
| Self::Index(_)
|
||||
| Self::OpaqueCast(_)
|
||||
| Self::Subslice { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Alias for projections as they appear in `UserTypeProjection`, where we
|
||||
/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
|
||||
pub type ProjectionKind = ProjectionElem<(), ()>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct PlaceRef<'tcx> {
|
||||
pub local: Local,
|
||||
pub projection: &'tcx [PlaceElem<'tcx>],
|
||||
}
|
||||
|
||||
// Once we stop implementing `Ord` for `DefId`,
|
||||
// this impl will be unnecessary. Until then, we'll
|
||||
// leave this impl in place to prevent re-adding a
|
||||
// dependency on the `Ord` impl for `DefId`
|
||||
impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
|
||||
|
||||
impl<'tcx> Place<'tcx> {
|
||||
// FIXME change this to a const fn by also making List::empty a const fn.
|
||||
pub fn return_place() -> Place<'tcx> {
|
||||
Place { local: RETURN_PLACE, projection: List::empty() }
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Place` contains a `Deref` projection.
|
||||
///
|
||||
/// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
|
||||
/// same region of memory as its base.
|
||||
pub fn is_indirect(&self) -> bool {
|
||||
self.projection.iter().any(|elem| elem.is_indirect())
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Place`'s first projection is `Deref`.
|
||||
///
|
||||
/// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
|
||||
/// `Deref` projections can only occur as the first projection. In that case this method
|
||||
/// is equivalent to `is_indirect`, but faster.
|
||||
pub fn is_indirect_first_projection(&self) -> bool {
|
||||
self.as_ref().is_indirect_first_projection()
|
||||
}
|
||||
|
||||
/// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
|
||||
/// a single deref of a local.
|
||||
#[inline(always)]
|
||||
pub fn local_or_deref_local(&self) -> Option<Local> {
|
||||
self.as_ref().local_or_deref_local()
|
||||
}
|
||||
|
||||
/// If this place represents a local variable like `_X` with no
|
||||
/// projections, return `Some(_X)`.
|
||||
#[inline(always)]
|
||||
pub fn as_local(&self) -> Option<Local> {
|
||||
self.as_ref().as_local()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_ref(&self) -> PlaceRef<'tcx> {
|
||||
PlaceRef { local: self.local, projection: &self.projection }
|
||||
}
|
||||
|
||||
/// Iterate over the projections in evaluation order, i.e., the first element is the base with
|
||||
/// its projection and then subsequently more projections are added.
|
||||
/// As a concrete example, given the place a.b.c, this would yield:
|
||||
/// - (a, .b)
|
||||
/// - (a.b, .c)
|
||||
///
|
||||
/// Given a place without projections, the iterator is empty.
|
||||
#[inline]
|
||||
pub fn iter_projections(
|
||||
self,
|
||||
) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
|
||||
self.as_ref().iter_projections()
|
||||
}
|
||||
|
||||
/// Generates a new place by appending `more_projections` to the existing ones
|
||||
/// and interning the result.
|
||||
pub fn project_deeper(self, more_projections: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
|
||||
if more_projections.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.as_ref().project_deeper(more_projections, tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Local> for Place<'_> {
|
||||
#[inline]
|
||||
fn from(local: Local) -> Self {
|
||||
Place { local, projection: List::empty() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> PlaceRef<'tcx> {
|
||||
/// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
|
||||
/// a single deref of a local.
|
||||
pub fn local_or_deref_local(&self) -> Option<Local> {
|
||||
match *self {
|
||||
PlaceRef { local, projection: [] }
|
||||
| PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Place` contains a `Deref` projection.
|
||||
///
|
||||
/// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
|
||||
/// same region of memory as its base.
|
||||
pub fn is_indirect(&self) -> bool {
|
||||
self.projection.iter().any(|elem| elem.is_indirect())
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Place`'s first projection is `Deref`.
|
||||
///
|
||||
/// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
|
||||
/// `Deref` projections can only occur as the first projection. In that case this method
|
||||
/// is equivalent to `is_indirect`, but faster.
|
||||
pub fn is_indirect_first_projection(&self) -> bool {
|
||||
// To make sure this is not accidentally used in wrong mir phase
|
||||
debug_assert!(
|
||||
self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref)
|
||||
);
|
||||
self.projection.first() == Some(&PlaceElem::Deref)
|
||||
}
|
||||
|
||||
/// If this place represents a local variable like `_X` with no
|
||||
/// projections, return `Some(_X)`.
|
||||
#[inline]
|
||||
pub fn as_local(&self) -> Option<Local> {
|
||||
match *self {
|
||||
PlaceRef { local, projection: [] } => Some(local),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
|
||||
if let &[ref proj_base @ .., elem] = self.projection {
|
||||
Some((PlaceRef { local: self.local, projection: proj_base }, elem))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over the projections in evaluation order, i.e., the first element is the base with
|
||||
/// its projection and then subsequently more projections are added.
|
||||
/// As a concrete example, given the place a.b.c, this would yield:
|
||||
/// - (a, .b)
|
||||
/// - (a.b, .c)
|
||||
///
|
||||
/// Given a place without projections, the iterator is empty.
|
||||
#[inline]
|
||||
pub fn iter_projections(
|
||||
self,
|
||||
) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
|
||||
self.projection.iter().enumerate().map(move |(i, proj)| {
|
||||
let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
|
||||
(base, *proj)
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates a new place by appending `more_projections` to the existing ones
|
||||
/// and interning the result.
|
||||
pub fn project_deeper(
|
||||
self,
|
||||
more_projections: &[PlaceElem<'tcx>],
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Place<'tcx> {
|
||||
let mut v: Vec<PlaceElem<'tcx>>;
|
||||
|
||||
let new_projections = if self.projection.is_empty() {
|
||||
more_projections
|
||||
} else {
|
||||
v = Vec::with_capacity(self.projection.len() + more_projections.len());
|
||||
v.extend(self.projection);
|
||||
v.extend(more_projections);
|
||||
&v
|
||||
};
|
||||
|
||||
Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Local> for PlaceRef<'_> {
|
||||
#[inline]
|
||||
fn from(local: Local) -> Self {
|
||||
PlaceRef { local, projection: &[] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Place<'_> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
self.as_ref().fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PlaceRef<'_> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
pre_fmt_projection(self.projection, fmt)?;
|
||||
write!(fmt, "{:?}", self.local)?;
|
||||
post_fmt_projection(self.projection, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
for &elem in projection.iter().rev() {
|
||||
match elem {
|
||||
ProjectionElem::OpaqueCast(_)
|
||||
| ProjectionElem::Downcast(_, _)
|
||||
| ProjectionElem::Field(_, _) => {
|
||||
write!(fmt, "(").unwrap();
|
||||
}
|
||||
ProjectionElem::Deref => {
|
||||
write!(fmt, "(*").unwrap();
|
||||
}
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
for &elem in projection.iter() {
|
||||
match elem {
|
||||
ProjectionElem::OpaqueCast(ty) => {
|
||||
write!(fmt, " as {ty})")?;
|
||||
}
|
||||
ProjectionElem::Downcast(Some(name), _index) => {
|
||||
write!(fmt, " as {name})")?;
|
||||
}
|
||||
ProjectionElem::Downcast(None, index) => {
|
||||
write!(fmt, " as variant#{index:?})")?;
|
||||
}
|
||||
ProjectionElem::Deref => {
|
||||
write!(fmt, ")")?;
|
||||
}
|
||||
ProjectionElem::Field(field, ty) => {
|
||||
with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
|
||||
}
|
||||
ProjectionElem::Index(ref index) => {
|
||||
write!(fmt, "[{index:?}]")?;
|
||||
}
|
||||
ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
|
||||
write!(fmt, "[{offset:?} of {min_length:?}]")?;
|
||||
}
|
||||
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
|
||||
write!(fmt, "[-{offset:?} of {min_length:?}]")?;
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
|
||||
write!(fmt, "[{from:?}:]")?;
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
|
||||
write!(fmt, "[:-{to:?}]")?;
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end: true } => {
|
||||
write!(fmt, "[{from:?}:-{to:?}]")?;
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end: false } => {
|
||||
write!(fmt, "[{from:?}..{to:?}]")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Scopes
|
||||
|
||||
@ -1968,345 +1386,12 @@ pub struct SourceScopeLocalData {
|
||||
pub safety: Safety,
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Operands
|
||||
|
||||
impl<'tcx> Debug for Operand<'tcx> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
use self::Operand::*;
|
||||
match *self {
|
||||
Constant(ref a) => write!(fmt, "{a:?}"),
|
||||
Copy(ref place) => write!(fmt, "{place:?}"),
|
||||
Move(ref place) => write!(fmt, "move {place:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Operand<'tcx> {
|
||||
/// Convenience helper to make a constant that refers to the fn
|
||||
/// with given `DefId` and args. Since this is used to synthesize
|
||||
/// MIR, assumes `user_ty` is None.
|
||||
pub fn function_handle(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
args: impl IntoIterator<Item = GenericArg<'tcx>>,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
let ty = Ty::new_fn_def(tcx, def_id, args);
|
||||
Operand::Constant(Box::new(Constant {
|
||||
span,
|
||||
user_ty: None,
|
||||
literal: ConstantKind::Val(ConstValue::ZeroSized, ty),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn is_move(&self) -> bool {
|
||||
matches!(self, Operand::Move(..))
|
||||
}
|
||||
|
||||
/// Convenience helper to make a literal-like constant from a given scalar value.
|
||||
/// Since this is used to synthesize MIR, assumes `user_ty` is None.
|
||||
pub fn const_from_scalar(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
val: Scalar,
|
||||
span: Span,
|
||||
) -> Operand<'tcx> {
|
||||
debug_assert!({
|
||||
let param_env_and_ty = ty::ParamEnv::empty().and(ty);
|
||||
let type_size = tcx
|
||||
.layout_of(param_env_and_ty)
|
||||
.unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}"))
|
||||
.size;
|
||||
let scalar_size = match val {
|
||||
Scalar::Int(int) => int.size(),
|
||||
_ => panic!("Invalid scalar type {val:?}"),
|
||||
};
|
||||
scalar_size == type_size
|
||||
});
|
||||
Operand::Constant(Box::new(Constant {
|
||||
span,
|
||||
user_ty: None,
|
||||
literal: ConstantKind::Val(ConstValue::Scalar(val), ty),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn to_copy(&self) -> Self {
|
||||
match *self {
|
||||
Operand::Copy(_) | Operand::Constant(_) => self.clone(),
|
||||
Operand::Move(place) => Operand::Copy(place),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a
|
||||
/// constant.
|
||||
pub fn place(&self) -> Option<Place<'tcx>> {
|
||||
match self {
|
||||
Operand::Copy(place) | Operand::Move(place) => Some(*place),
|
||||
Operand::Constant(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a
|
||||
/// place.
|
||||
pub fn constant(&self) -> Option<&Constant<'tcx>> {
|
||||
match self {
|
||||
Operand::Constant(x) => Some(&**x),
|
||||
Operand::Copy(_) | Operand::Move(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the `ty::FnDef` from an operand if it's a constant function item.
|
||||
///
|
||||
/// While this is unlikely in general, it's the normal case of what you'll
|
||||
/// find as the `func` in a [`TerminatorKind::Call`].
|
||||
pub fn const_fn_def(&self) -> Option<(DefId, GenericArgsRef<'tcx>)> {
|
||||
let const_ty = self.constant()?.literal.ty();
|
||||
if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Rvalues
|
||||
|
||||
impl<'tcx> Rvalue<'tcx> {
|
||||
/// Returns true if rvalue can be safely removed when the result is unused.
|
||||
#[inline]
|
||||
pub fn is_safe_to_remove(&self) -> bool {
|
||||
match self {
|
||||
// Pointer to int casts may be side-effects due to exposing the provenance.
|
||||
// While the model is undecided, we should be conservative. See
|
||||
// <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
|
||||
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
|
||||
|
||||
Rvalue::Use(_)
|
||||
| Rvalue::CopyForDeref(_)
|
||||
| Rvalue::Repeat(_, _)
|
||||
| Rvalue::Ref(_, _, _)
|
||||
| Rvalue::ThreadLocalRef(_)
|
||||
| Rvalue::AddressOf(_, _)
|
||||
| Rvalue::Len(_)
|
||||
| Rvalue::Cast(
|
||||
CastKind::IntToInt
|
||||
| CastKind::FloatToInt
|
||||
| CastKind::FloatToFloat
|
||||
| CastKind::IntToFloat
|
||||
| CastKind::FnPtrToPtr
|
||||
| CastKind::PtrToPtr
|
||||
| CastKind::PointerCoercion(_)
|
||||
| CastKind::PointerFromExposedAddress
|
||||
| CastKind::DynStar
|
||||
| CastKind::Transmute,
|
||||
_,
|
||||
_,
|
||||
)
|
||||
| Rvalue::BinaryOp(_, _)
|
||||
| Rvalue::CheckedBinaryOp(_, _)
|
||||
| Rvalue::NullaryOp(_, _)
|
||||
| Rvalue::UnaryOp(_, _)
|
||||
| Rvalue::Discriminant(_)
|
||||
| Rvalue::Aggregate(_, _)
|
||||
| Rvalue::ShallowInitBox(_, _) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BorrowKind {
|
||||
pub fn mutability(&self) -> Mutability {
|
||||
match *self {
|
||||
BorrowKind::Shared | BorrowKind::Shallow => Mutability::Not,
|
||||
BorrowKind::Mut { .. } => Mutability::Mut,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allows_two_phase_borrow(&self) -> bool {
|
||||
match *self {
|
||||
BorrowKind::Shared
|
||||
| BorrowKind::Shallow
|
||||
| BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
|
||||
false
|
||||
}
|
||||
BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Debug for Rvalue<'tcx> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
use self::Rvalue::*;
|
||||
|
||||
match *self {
|
||||
Use(ref place) => write!(fmt, "{place:?}"),
|
||||
Repeat(ref a, b) => {
|
||||
write!(fmt, "[{a:?}; ")?;
|
||||
pretty_print_const(b, fmt, false)?;
|
||||
write!(fmt, "]")
|
||||
}
|
||||
Len(ref a) => write!(fmt, "Len({a:?})"),
|
||||
Cast(ref kind, ref place, ref ty) => {
|
||||
with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
|
||||
}
|
||||
BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
|
||||
CheckedBinaryOp(ref op, box (ref a, ref b)) => {
|
||||
write!(fmt, "Checked{op:?}({a:?}, {b:?})")
|
||||
}
|
||||
UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
|
||||
Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
|
||||
NullaryOp(ref op, ref t) => {
|
||||
let t = with_no_trimmed_paths!(format!("{}", t));
|
||||
match op {
|
||||
NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
|
||||
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
|
||||
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
|
||||
}
|
||||
}
|
||||
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
||||
let muta = tcx.static_mutability(did).unwrap().prefix_str();
|
||||
write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
|
||||
}),
|
||||
Ref(region, borrow_kind, ref place) => {
|
||||
let kind_str = match borrow_kind {
|
||||
BorrowKind::Shared => "",
|
||||
BorrowKind::Shallow => "shallow ",
|
||||
BorrowKind::Mut { .. } => "mut ",
|
||||
};
|
||||
|
||||
// When printing regions, add trailing space if necessary.
|
||||
let print_region = ty::tls::with(|tcx| {
|
||||
tcx.sess.verbose() || tcx.sess.opts.unstable_opts.identify_regions
|
||||
});
|
||||
let region = if print_region {
|
||||
let mut region = region.to_string();
|
||||
if !region.is_empty() {
|
||||
region.push(' ');
|
||||
}
|
||||
region
|
||||
} else {
|
||||
// Do not even print 'static
|
||||
String::new()
|
||||
};
|
||||
write!(fmt, "&{region}{kind_str}{place:?}")
|
||||
}
|
||||
|
||||
CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
|
||||
|
||||
AddressOf(mutability, ref place) => {
|
||||
let kind_str = match mutability {
|
||||
Mutability::Mut => "mut",
|
||||
Mutability::Not => "const",
|
||||
};
|
||||
|
||||
write!(fmt, "&raw {kind_str} {place:?}")
|
||||
}
|
||||
|
||||
Aggregate(ref kind, ref places) => {
|
||||
let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
|
||||
let mut tuple_fmt = fmt.debug_tuple(name);
|
||||
for place in places {
|
||||
tuple_fmt.field(place);
|
||||
}
|
||||
tuple_fmt.finish()
|
||||
};
|
||||
|
||||
match **kind {
|
||||
AggregateKind::Array(_) => write!(fmt, "{places:?}"),
|
||||
|
||||
AggregateKind::Tuple => {
|
||||
if places.is_empty() {
|
||||
write!(fmt, "()")
|
||||
} else {
|
||||
fmt_tuple(fmt, "")
|
||||
}
|
||||
}
|
||||
|
||||
AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
|
||||
ty::tls::with(|tcx| {
|
||||
let variant_def = &tcx.adt_def(adt_did).variant(variant);
|
||||
let args = tcx.lift(args).expect("could not lift for printing");
|
||||
let name = FmtPrinter::new(tcx, Namespace::ValueNS)
|
||||
.print_def_path(variant_def.def_id, args)?
|
||||
.into_buffer();
|
||||
|
||||
match variant_def.ctor_kind() {
|
||||
Some(CtorKind::Const) => fmt.write_str(&name),
|
||||
Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
|
||||
None => {
|
||||
let mut struct_fmt = fmt.debug_struct(&name);
|
||||
for (field, place) in iter::zip(&variant_def.fields, places) {
|
||||
struct_fmt.field(field.name.as_str(), place);
|
||||
}
|
||||
struct_fmt.finish()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
AggregateKind::Closure(def_id, args) => ty::tls::with(|tcx| {
|
||||
let name = if tcx.sess.opts.unstable_opts.span_free_formats {
|
||||
let args = tcx.lift(args).unwrap();
|
||||
format!("[closure@{}]", tcx.def_path_str_with_args(def_id, args),)
|
||||
} else {
|
||||
let span = tcx.def_span(def_id);
|
||||
format!(
|
||||
"[closure@{}]",
|
||||
tcx.sess.source_map().span_to_diagnostic_string(span)
|
||||
)
|
||||
};
|
||||
let mut struct_fmt = fmt.debug_struct(&name);
|
||||
|
||||
// FIXME(project-rfc-2229#48): This should be a list of capture names/places
|
||||
if let Some(def_id) = def_id.as_local()
|
||||
&& let Some(upvars) = tcx.upvars_mentioned(def_id)
|
||||
{
|
||||
for (&var_id, place) in iter::zip(upvars.keys(), places) {
|
||||
let var_name = tcx.hir().name(var_id);
|
||||
struct_fmt.field(var_name.as_str(), place);
|
||||
}
|
||||
} else {
|
||||
for (index, place) in places.iter().enumerate() {
|
||||
struct_fmt.field(&format!("{index}"), place);
|
||||
}
|
||||
}
|
||||
|
||||
struct_fmt.finish()
|
||||
}),
|
||||
|
||||
AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
|
||||
let name = format!("[generator@{:?}]", tcx.def_span(def_id));
|
||||
let mut struct_fmt = fmt.debug_struct(&name);
|
||||
|
||||
// FIXME(project-rfc-2229#48): This should be a list of capture names/places
|
||||
if let Some(def_id) = def_id.as_local()
|
||||
&& let Some(upvars) = tcx.upvars_mentioned(def_id)
|
||||
{
|
||||
for (&var_id, place) in iter::zip(upvars.keys(), places) {
|
||||
let var_name = tcx.hir().name(var_id);
|
||||
struct_fmt.field(var_name.as_str(), place);
|
||||
}
|
||||
} else {
|
||||
for (index, place) in places.iter().enumerate() {
|
||||
struct_fmt.field(&format!("{index}"), place);
|
||||
}
|
||||
}
|
||||
|
||||
struct_fmt.finish()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
ShallowInitBox(ref place, ref ty) => {
|
||||
with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of projections into user types.
|
||||
///
|
||||
/// They are projections because a binding can occur a part of a
|
||||
/// parent pattern that has been ascribed a type.
|
||||
///
|
||||
/// Its a collection because there can be multiple type ascriptions on
|
||||
/// It's a collection because there can be multiple type ascriptions on
|
||||
/// the path from the root of the pattern down to the binding itself.
|
||||
///
|
||||
/// An example:
|
||||
|
File diff suppressed because it is too large
Load Diff
441
compiler/rustc_middle/src/mir/statement.rs
Normal file
441
compiler/rustc_middle/src/mir/statement.rs
Normal file
@ -0,0 +1,441 @@
|
||||
/// Functionality for statements, operands, places, and things that appear in them.
|
||||
use super::*;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Statements
|
||||
|
||||
/// A statement in a basic block, including information about its source code.
|
||||
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct Statement<'tcx> {
|
||||
pub source_info: SourceInfo,
|
||||
pub kind: StatementKind<'tcx>,
|
||||
}
|
||||
|
||||
impl Statement<'_> {
|
||||
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
|
||||
/// invalidating statement indices in `Location`s.
|
||||
pub fn make_nop(&mut self) {
|
||||
self.kind = StatementKind::Nop
|
||||
}
|
||||
|
||||
/// Changes a statement to a nop and returns the original statement.
|
||||
#[must_use = "If you don't need the statement, use `make_nop` instead"]
|
||||
pub fn replace_nop(&mut self) -> Self {
|
||||
Statement {
|
||||
source_info: self.source_info,
|
||||
kind: mem::replace(&mut self.kind, StatementKind::Nop),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> StatementKind<'tcx> {
|
||||
pub fn as_assign_mut(&mut self) -> Option<&mut (Place<'tcx>, Rvalue<'tcx>)> {
|
||||
match self {
|
||||
StatementKind::Assign(x) => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_assign(&self) -> Option<&(Place<'tcx>, Rvalue<'tcx>)> {
|
||||
match self {
|
||||
StatementKind::Assign(x) => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Places
|
||||
|
||||
impl<V, T> ProjectionElem<V, T> {
|
||||
/// Returns `true` if the target of this projection may refer to a different region of memory
|
||||
/// than the base.
|
||||
fn is_indirect(&self) -> bool {
|
||||
match self {
|
||||
Self::Deref => true,
|
||||
|
||||
Self::Field(_, _)
|
||||
| Self::Index(_)
|
||||
| Self::OpaqueCast(_)
|
||||
| Self::ConstantIndex { .. }
|
||||
| Self::Subslice { .. }
|
||||
| Self::Downcast(_, _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the target of this projection always refers to the same memory region
|
||||
/// whatever the state of the program.
|
||||
pub fn is_stable_offset(&self) -> bool {
|
||||
match self {
|
||||
Self::Deref | Self::Index(_) => false,
|
||||
Self::Field(_, _)
|
||||
| Self::OpaqueCast(_)
|
||||
| Self::ConstantIndex { .. }
|
||||
| Self::Subslice { .. }
|
||||
| Self::Downcast(_, _) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
|
||||
pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
|
||||
matches!(*self, Self::Downcast(_, x) if x == v)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a `Field` projection with the given index.
|
||||
pub fn is_field_to(&self, f: FieldIdx) -> bool {
|
||||
matches!(*self, Self::Field(x, _) if x == f)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is accepted inside `VarDebugInfoContents::Place`.
|
||||
pub fn can_use_in_debuginfo(&self) -> bool {
|
||||
match self {
|
||||
Self::ConstantIndex { from_end: false, .. }
|
||||
| Self::Deref
|
||||
| Self::Downcast(_, _)
|
||||
| Self::Field(_, _) => true,
|
||||
Self::ConstantIndex { from_end: true, .. }
|
||||
| Self::Index(_)
|
||||
| Self::OpaqueCast(_)
|
||||
| Self::Subslice { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Alias for projections as they appear in `UserTypeProjection`, where we
|
||||
/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
|
||||
pub type ProjectionKind = ProjectionElem<(), ()>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct PlaceRef<'tcx> {
|
||||
pub local: Local,
|
||||
pub projection: &'tcx [PlaceElem<'tcx>],
|
||||
}
|
||||
|
||||
// Once we stop implementing `Ord` for `DefId`,
|
||||
// this impl will be unnecessary. Until then, we'll
|
||||
// leave this impl in place to prevent re-adding a
|
||||
// dependency on the `Ord` impl for `DefId`
|
||||
impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
|
||||
|
||||
impl<'tcx> Place<'tcx> {
|
||||
// FIXME change this to a const fn by also making List::empty a const fn.
|
||||
pub fn return_place() -> Place<'tcx> {
|
||||
Place { local: RETURN_PLACE, projection: List::empty() }
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Place` contains a `Deref` projection.
|
||||
///
|
||||
/// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
|
||||
/// same region of memory as its base.
|
||||
pub fn is_indirect(&self) -> bool {
|
||||
self.projection.iter().any(|elem| elem.is_indirect())
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Place`'s first projection is `Deref`.
|
||||
///
|
||||
/// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
|
||||
/// `Deref` projections can only occur as the first projection. In that case this method
|
||||
/// is equivalent to `is_indirect`, but faster.
|
||||
pub fn is_indirect_first_projection(&self) -> bool {
|
||||
self.as_ref().is_indirect_first_projection()
|
||||
}
|
||||
|
||||
/// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
|
||||
/// a single deref of a local.
|
||||
#[inline(always)]
|
||||
pub fn local_or_deref_local(&self) -> Option<Local> {
|
||||
self.as_ref().local_or_deref_local()
|
||||
}
|
||||
|
||||
/// If this place represents a local variable like `_X` with no
|
||||
/// projections, return `Some(_X)`.
|
||||
#[inline(always)]
|
||||
pub fn as_local(&self) -> Option<Local> {
|
||||
self.as_ref().as_local()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_ref(&self) -> PlaceRef<'tcx> {
|
||||
PlaceRef { local: self.local, projection: &self.projection }
|
||||
}
|
||||
|
||||
/// Iterate over the projections in evaluation order, i.e., the first element is the base with
|
||||
/// its projection and then subsequently more projections are added.
|
||||
/// As a concrete example, given the place a.b.c, this would yield:
|
||||
/// - (a, .b)
|
||||
/// - (a.b, .c)
|
||||
///
|
||||
/// Given a place without projections, the iterator is empty.
|
||||
#[inline]
|
||||
pub fn iter_projections(
|
||||
self,
|
||||
) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
|
||||
self.as_ref().iter_projections()
|
||||
}
|
||||
|
||||
/// Generates a new place by appending `more_projections` to the existing ones
|
||||
/// and interning the result.
|
||||
pub fn project_deeper(self, more_projections: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
|
||||
if more_projections.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.as_ref().project_deeper(more_projections, tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Local> for Place<'_> {
|
||||
#[inline]
|
||||
fn from(local: Local) -> Self {
|
||||
Place { local, projection: List::empty() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> PlaceRef<'tcx> {
|
||||
/// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
|
||||
/// a single deref of a local.
|
||||
pub fn local_or_deref_local(&self) -> Option<Local> {
|
||||
match *self {
|
||||
PlaceRef { local, projection: [] }
|
||||
| PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Place` contains a `Deref` projection.
|
||||
///
|
||||
/// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
|
||||
/// same region of memory as its base.
|
||||
pub fn is_indirect(&self) -> bool {
|
||||
self.projection.iter().any(|elem| elem.is_indirect())
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Place`'s first projection is `Deref`.
|
||||
///
|
||||
/// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
|
||||
/// `Deref` projections can only occur as the first projection. In that case this method
|
||||
/// is equivalent to `is_indirect`, but faster.
|
||||
pub fn is_indirect_first_projection(&self) -> bool {
|
||||
// To make sure this is not accidentally used in wrong mir phase
|
||||
debug_assert!(
|
||||
self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref)
|
||||
);
|
||||
self.projection.first() == Some(&PlaceElem::Deref)
|
||||
}
|
||||
|
||||
/// If this place represents a local variable like `_X` with no
|
||||
/// projections, return `Some(_X)`.
|
||||
#[inline]
|
||||
pub fn as_local(&self) -> Option<Local> {
|
||||
match *self {
|
||||
PlaceRef { local, projection: [] } => Some(local),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
|
||||
if let &[ref proj_base @ .., elem] = self.projection {
|
||||
Some((PlaceRef { local: self.local, projection: proj_base }, elem))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over the projections in evaluation order, i.e., the first element is the base with
|
||||
/// its projection and then subsequently more projections are added.
|
||||
/// As a concrete example, given the place a.b.c, this would yield:
|
||||
/// - (a, .b)
|
||||
/// - (a.b, .c)
|
||||
///
|
||||
/// Given a place without projections, the iterator is empty.
|
||||
#[inline]
|
||||
pub fn iter_projections(
|
||||
self,
|
||||
) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
|
||||
self.projection.iter().enumerate().map(move |(i, proj)| {
|
||||
let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
|
||||
(base, *proj)
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates a new place by appending `more_projections` to the existing ones
|
||||
/// and interning the result.
|
||||
pub fn project_deeper(
|
||||
self,
|
||||
more_projections: &[PlaceElem<'tcx>],
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Place<'tcx> {
|
||||
let mut v: Vec<PlaceElem<'tcx>>;
|
||||
|
||||
let new_projections = if self.projection.is_empty() {
|
||||
more_projections
|
||||
} else {
|
||||
v = Vec::with_capacity(self.projection.len() + more_projections.len());
|
||||
v.extend(self.projection);
|
||||
v.extend(more_projections);
|
||||
&v
|
||||
};
|
||||
|
||||
Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Local> for PlaceRef<'_> {
|
||||
#[inline]
|
||||
fn from(local: Local) -> Self {
|
||||
PlaceRef { local, projection: &[] }
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Operands
|
||||
|
||||
impl<'tcx> Operand<'tcx> {
|
||||
/// Convenience helper to make a constant that refers to the fn
|
||||
/// with given `DefId` and args. Since this is used to synthesize
|
||||
/// MIR, assumes `user_ty` is None.
|
||||
pub fn function_handle(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
args: impl IntoIterator<Item = GenericArg<'tcx>>,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
let ty = Ty::new_fn_def(tcx, def_id, args);
|
||||
Operand::Constant(Box::new(Constant {
|
||||
span,
|
||||
user_ty: None,
|
||||
literal: ConstantKind::Val(ConstValue::ZeroSized, ty),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn is_move(&self) -> bool {
|
||||
matches!(self, Operand::Move(..))
|
||||
}
|
||||
|
||||
/// Convenience helper to make a literal-like constant from a given scalar value.
|
||||
/// Since this is used to synthesize MIR, assumes `user_ty` is None.
|
||||
pub fn const_from_scalar(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
val: Scalar,
|
||||
span: Span,
|
||||
) -> Operand<'tcx> {
|
||||
debug_assert!({
|
||||
let param_env_and_ty = ty::ParamEnv::empty().and(ty);
|
||||
let type_size = tcx
|
||||
.layout_of(param_env_and_ty)
|
||||
.unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}"))
|
||||
.size;
|
||||
let scalar_size = match val {
|
||||
Scalar::Int(int) => int.size(),
|
||||
_ => panic!("Invalid scalar type {val:?}"),
|
||||
};
|
||||
scalar_size == type_size
|
||||
});
|
||||
Operand::Constant(Box::new(Constant {
|
||||
span,
|
||||
user_ty: None,
|
||||
literal: ConstantKind::Val(ConstValue::Scalar(val), ty),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn to_copy(&self) -> Self {
|
||||
match *self {
|
||||
Operand::Copy(_) | Operand::Constant(_) => self.clone(),
|
||||
Operand::Move(place) => Operand::Copy(place),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a
|
||||
/// constant.
|
||||
pub fn place(&self) -> Option<Place<'tcx>> {
|
||||
match self {
|
||||
Operand::Copy(place) | Operand::Move(place) => Some(*place),
|
||||
Operand::Constant(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a
|
||||
/// place.
|
||||
pub fn constant(&self) -> Option<&Constant<'tcx>> {
|
||||
match self {
|
||||
Operand::Constant(x) => Some(&**x),
|
||||
Operand::Copy(_) | Operand::Move(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the `ty::FnDef` from an operand if it's a constant function item.
|
||||
///
|
||||
/// While this is unlikely in general, it's the normal case of what you'll
|
||||
/// find as the `func` in a [`TerminatorKind::Call`].
|
||||
pub fn const_fn_def(&self) -> Option<(DefId, GenericArgsRef<'tcx>)> {
|
||||
let const_ty = self.constant()?.literal.ty();
|
||||
if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// Rvalues
|
||||
|
||||
impl<'tcx> Rvalue<'tcx> {
|
||||
/// Returns true if rvalue can be safely removed when the result is unused.
|
||||
#[inline]
|
||||
pub fn is_safe_to_remove(&self) -> bool {
|
||||
match self {
|
||||
// Pointer to int casts may be side-effects due to exposing the provenance.
|
||||
// While the model is undecided, we should be conservative. See
|
||||
// <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
|
||||
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
|
||||
|
||||
Rvalue::Use(_)
|
||||
| Rvalue::CopyForDeref(_)
|
||||
| Rvalue::Repeat(_, _)
|
||||
| Rvalue::Ref(_, _, _)
|
||||
| Rvalue::ThreadLocalRef(_)
|
||||
| Rvalue::AddressOf(_, _)
|
||||
| Rvalue::Len(_)
|
||||
| Rvalue::Cast(
|
||||
CastKind::IntToInt
|
||||
| CastKind::FloatToInt
|
||||
| CastKind::FloatToFloat
|
||||
| CastKind::IntToFloat
|
||||
| CastKind::FnPtrToPtr
|
||||
| CastKind::PtrToPtr
|
||||
| CastKind::PointerCoercion(_)
|
||||
| CastKind::PointerFromExposedAddress
|
||||
| CastKind::DynStar
|
||||
| CastKind::Transmute,
|
||||
_,
|
||||
_,
|
||||
)
|
||||
| Rvalue::BinaryOp(_, _)
|
||||
| Rvalue::CheckedBinaryOp(_, _)
|
||||
| Rvalue::NullaryOp(_, _)
|
||||
| Rvalue::UnaryOp(_, _)
|
||||
| Rvalue::Discriminant(_)
|
||||
| Rvalue::Aggregate(_, _)
|
||||
| Rvalue::ShallowInitBox(_, _) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BorrowKind {
|
||||
pub fn mutability(&self) -> Mutability {
|
||||
match *self {
|
||||
BorrowKind::Shared | BorrowKind::Shallow => Mutability::Not,
|
||||
BorrowKind::Mut { .. } => Mutability::Mut,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allows_two_phase_borrow(&self) -> bool {
|
||||
match *self {
|
||||
BorrowKind::Shared
|
||||
| BorrowKind::Shallow
|
||||
| BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
|
||||
false
|
||||
}
|
||||
BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
//! This is in a dedicated file so that changes to this file can be reviewed more carefully.
|
||||
//! The intention is that this file only contains datatype declarations, no code.
|
||||
|
||||
use super::{BasicBlock, Constant, Local, SwitchTargets, UserTypeProjection};
|
||||
use super::{BasicBlock, Constant, Local, UserTypeProjection};
|
||||
|
||||
use crate::mir::coverage::{CodeRegion, CoverageKind};
|
||||
use crate::traits::Reveal;
|
||||
@ -24,6 +24,7 @@ use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::asm::InlineAsmRegOrRegClass;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// Represents the "flavors" of MIR.
|
||||
///
|
||||
@ -828,6 +829,27 @@ impl TerminatorKind<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
|
||||
pub struct SwitchTargets {
|
||||
/// Possible values. The locations to branch to in each case
|
||||
/// are found in the corresponding indices from the `targets` vector.
|
||||
pub(super) 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.
|
||||
pub(super) targets: SmallVec<[BasicBlock; 2]>,
|
||||
}
|
||||
|
||||
/// Action to be taken when a stack unwind happens.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
#[derive(TypeFoldable, TypeVisitable)]
|
||||
|
@ -1,39 +1,16 @@
|
||||
/// Functionality for terminators and helper types that appear in terminators.
|
||||
use rustc_hir::LangItem;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind, UnwindAction};
|
||||
use rustc_ast::InlineAsmTemplatePiece;
|
||||
pub use rustc_ast::Mutability;
|
||||
use rustc_macros::HashStable;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::iter;
|
||||
use std::slice;
|
||||
|
||||
pub use super::query::*;
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
|
||||
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<[BasicBlock; 2]>,
|
||||
}
|
||||
|
||||
impl SwitchTargets {
|
||||
/// Creates switch targets from an iterator of values and target blocks.
|
||||
///
|
||||
@ -135,6 +112,168 @@ impl UnwindTerminateReason {
|
||||
}
|
||||
}
|
||||
|
||||
impl<O> AssertKind<O> {
|
||||
/// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
|
||||
pub fn is_optional_overflow_check(&self) -> bool {
|
||||
use AssertKind::*;
|
||||
use BinOp::*;
|
||||
matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
|
||||
}
|
||||
|
||||
/// Get the message that is printed at runtime when this assertion fails.
|
||||
///
|
||||
/// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
|
||||
/// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
|
||||
/// instead of printing a static message.
|
||||
pub fn description(&self) -> &'static str {
|
||||
use AssertKind::*;
|
||||
match self {
|
||||
Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
|
||||
Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
|
||||
Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
|
||||
Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
|
||||
Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
|
||||
OverflowNeg(_) => "attempt to negate with overflow",
|
||||
Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
|
||||
Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
|
||||
Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
|
||||
DivisionByZero(_) => "attempt to divide by zero",
|
||||
RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
|
||||
ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
|
||||
ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
|
||||
ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
|
||||
ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
|
||||
BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
|
||||
bug!("Unexpected AssertKind")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
|
||||
///
|
||||
/// Needs to be kept in sync with the run-time behavior (which is defined by
|
||||
/// `AssertKind::description` and the lang items mentioned in its docs).
|
||||
/// Note that we deliberately show more details here than we do at runtime, such as the actual
|
||||
/// numbers that overflowed -- it is much easier to do so here than at runtime.
|
||||
pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
|
||||
where
|
||||
O: Debug,
|
||||
{
|
||||
use AssertKind::*;
|
||||
match self {
|
||||
BoundsCheck { ref len, ref index } => write!(
|
||||
f,
|
||||
"\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
|
||||
),
|
||||
|
||||
OverflowNeg(op) => {
|
||||
write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
|
||||
}
|
||||
DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
|
||||
RemainderByZero(op) => write!(
|
||||
f,
|
||||
"\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
|
||||
),
|
||||
Overflow(BinOp::Add, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Sub, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Mul, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Div, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Rem, l, r) => write!(
|
||||
f,
|
||||
"\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
|
||||
),
|
||||
Overflow(BinOp::Shr, _, r) => {
|
||||
write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
|
||||
}
|
||||
Overflow(BinOp::Shl, _, r) => {
|
||||
write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
|
||||
}
|
||||
MisalignedPointerDereference { required, found } => {
|
||||
write!(
|
||||
f,
|
||||
"\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
|
||||
)
|
||||
}
|
||||
_ => write!(f, "\"{}\"", self.description()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
|
||||
///
|
||||
/// Needs to be kept in sync with the run-time behavior (which is defined by
|
||||
/// `AssertKind::description` and the lang items mentioned in its docs).
|
||||
/// Note that we deliberately show more details here than we do at runtime, such as the actual
|
||||
/// numbers that overflowed -- it is much easier to do so here than at runtime.
|
||||
pub fn diagnostic_message(&self) -> DiagnosticMessage {
|
||||
use crate::fluent_generated::*;
|
||||
use AssertKind::*;
|
||||
|
||||
match self {
|
||||
BoundsCheck { .. } => middle_bounds_check,
|
||||
Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
|
||||
Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
|
||||
Overflow(_, _, _) => middle_assert_op_overflow,
|
||||
OverflowNeg(_) => middle_assert_overflow_neg,
|
||||
DivisionByZero(_) => middle_assert_divide_by_zero,
|
||||
RemainderByZero(_) => middle_assert_remainder_by_zero,
|
||||
ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
|
||||
ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
|
||||
ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
|
||||
ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
|
||||
|
||||
MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
|
||||
where
|
||||
O: fmt::Debug,
|
||||
{
|
||||
use AssertKind::*;
|
||||
|
||||
macro_rules! add {
|
||||
($name: expr, $value: expr) => {
|
||||
adder($name.into(), $value.into_diagnostic_arg());
|
||||
};
|
||||
}
|
||||
|
||||
match self {
|
||||
BoundsCheck { len, index } => {
|
||||
add!("len", format!("{len:?}"));
|
||||
add!("index", format!("{index:?}"));
|
||||
}
|
||||
Overflow(BinOp::Shl | BinOp::Shr, _, val)
|
||||
| DivisionByZero(val)
|
||||
| RemainderByZero(val)
|
||||
| OverflowNeg(val) => {
|
||||
add!("val", format!("{val:#?}"));
|
||||
}
|
||||
Overflow(binop, left, right) => {
|
||||
add!("op", binop.to_hir_binop().as_str());
|
||||
add!("left", format!("{left:#?}"));
|
||||
add!("right", format!("{right:#?}"));
|
||||
}
|
||||
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
|
||||
MisalignedPointerDereference { required, found } => {
|
||||
add!("required", format!("{required:#?}"));
|
||||
add!("found", format!("{found:#?}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct Terminator<'tcx> {
|
||||
pub source_info: SourceInfo,
|
||||
@ -299,187 +438,6 @@ impl<'tcx> TerminatorKind<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Debug for TerminatorKind<'tcx> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
self.fmt_head(fmt)?;
|
||||
let successor_count = self.successors().count();
|
||||
let labels = self.fmt_successor_labels();
|
||||
assert_eq!(successor_count, labels.len());
|
||||
|
||||
// `Cleanup` is already included in successors
|
||||
let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
|
||||
let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
|
||||
write!(fmt, "unwind ")?;
|
||||
match self.unwind() {
|
||||
// Not needed or included in successors
|
||||
None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
|
||||
Some(UnwindAction::Continue) => write!(fmt, "continue"),
|
||||
Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
|
||||
Some(UnwindAction::Terminate(reason)) => {
|
||||
write!(fmt, "terminate({})", reason.as_short_str())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match (successor_count, show_unwind) {
|
||||
(0, false) => Ok(()),
|
||||
(0, true) => {
|
||||
write!(fmt, " -> ")?;
|
||||
fmt_unwind(fmt)
|
||||
}
|
||||
(1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
|
||||
_ => {
|
||||
write!(fmt, " -> [")?;
|
||||
for (i, target) in self.successors().enumerate() {
|
||||
if i > 0 {
|
||||
write!(fmt, ", ")?;
|
||||
}
|
||||
write!(fmt, "{}: {:?}", labels[i], target)?;
|
||||
}
|
||||
if show_unwind {
|
||||
write!(fmt, ", ")?;
|
||||
fmt_unwind(fmt)?;
|
||||
}
|
||||
write!(fmt, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TerminatorKind<'tcx> {
|
||||
/// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
|
||||
/// successor basic block, if any. The only information not included is the list of possible
|
||||
/// successors, which may be rendered differently between the text and the graphviz format.
|
||||
pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
|
||||
use self::TerminatorKind::*;
|
||||
match self {
|
||||
Goto { .. } => write!(fmt, "goto"),
|
||||
SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
|
||||
Return => write!(fmt, "return"),
|
||||
GeneratorDrop => write!(fmt, "generator_drop"),
|
||||
UnwindResume => write!(fmt, "resume"),
|
||||
UnwindTerminate(reason) => {
|
||||
write!(fmt, "abort({})", reason.as_short_str())
|
||||
}
|
||||
Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
|
||||
Unreachable => write!(fmt, "unreachable"),
|
||||
Drop { place, .. } => write!(fmt, "drop({place:?})"),
|
||||
Call { func, args, destination, .. } => {
|
||||
write!(fmt, "{destination:?} = ")?;
|
||||
write!(fmt, "{func:?}(")?;
|
||||
for (index, arg) in args.iter().enumerate() {
|
||||
if index > 0 {
|
||||
write!(fmt, ", ")?;
|
||||
}
|
||||
write!(fmt, "{arg:?}")?;
|
||||
}
|
||||
write!(fmt, ")")
|
||||
}
|
||||
Assert { cond, expected, msg, .. } => {
|
||||
write!(fmt, "assert(")?;
|
||||
if !expected {
|
||||
write!(fmt, "!")?;
|
||||
}
|
||||
write!(fmt, "{cond:?}, ")?;
|
||||
msg.fmt_assert_args(fmt)?;
|
||||
write!(fmt, ")")
|
||||
}
|
||||
FalseEdge { .. } => write!(fmt, "falseEdge"),
|
||||
FalseUnwind { .. } => write!(fmt, "falseUnwind"),
|
||||
InlineAsm { template, ref operands, options, .. } => {
|
||||
write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
|
||||
for op in operands {
|
||||
write!(fmt, ", ")?;
|
||||
let print_late = |&late| if late { "late" } else { "" };
|
||||
match op {
|
||||
InlineAsmOperand::In { reg, value } => {
|
||||
write!(fmt, "in({reg}) {value:?}")?;
|
||||
}
|
||||
InlineAsmOperand::Out { reg, late, place: Some(place) } => {
|
||||
write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
|
||||
}
|
||||
InlineAsmOperand::Out { reg, late, place: None } => {
|
||||
write!(fmt, "{}out({}) _", print_late(late), reg)?;
|
||||
}
|
||||
InlineAsmOperand::InOut {
|
||||
reg,
|
||||
late,
|
||||
in_value,
|
||||
out_place: Some(out_place),
|
||||
} => {
|
||||
write!(
|
||||
fmt,
|
||||
"in{}out({}) {:?} => {:?}",
|
||||
print_late(late),
|
||||
reg,
|
||||
in_value,
|
||||
out_place
|
||||
)?;
|
||||
}
|
||||
InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
|
||||
write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
|
||||
}
|
||||
InlineAsmOperand::Const { value } => {
|
||||
write!(fmt, "const {value:?}")?;
|
||||
}
|
||||
InlineAsmOperand::SymFn { value } => {
|
||||
write!(fmt, "sym_fn {value:?}")?;
|
||||
}
|
||||
InlineAsmOperand::SymStatic { def_id } => {
|
||||
write!(fmt, "sym_static {def_id:?}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(fmt, ", options({options:?}))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of labels for the edges to the successor basic blocks.
|
||||
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
|
||||
use self::TerminatorKind::*;
|
||||
match *self {
|
||||
Return | UnwindResume | UnwindTerminate(_) | Unreachable | GeneratorDrop => vec![],
|
||||
Goto { .. } => vec!["".into()],
|
||||
SwitchInt { ref targets, .. } => targets
|
||||
.values
|
||||
.iter()
|
||||
.map(|&u| Cow::Owned(u.to_string()))
|
||||
.chain(iter::once("otherwise".into()))
|
||||
.collect(),
|
||||
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![],
|
||||
Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
|
||||
Yield { drop: None, .. } => vec!["resume".into()],
|
||||
Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
|
||||
Drop { unwind: _, .. } => vec!["return".into()],
|
||||
Assert { unwind: UnwindAction::Cleanup(_), .. } => {
|
||||
vec!["success".into(), "unwind".into()]
|
||||
}
|
||||
Assert { unwind: _, .. } => vec!["success".into()],
|
||||
FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
|
||||
FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
|
||||
vec!["real".into(), "unwind".into()]
|
||||
}
|
||||
FalseUnwind { unwind: _, .. } => vec!["real".into()],
|
||||
InlineAsm { destination: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
|
||||
vec!["return".into(), "unwind".into()]
|
||||
}
|
||||
InlineAsm { destination: Some(_), unwind: _, .. } => {
|
||||
vec!["return".into()]
|
||||
}
|
||||
InlineAsm { destination: None, unwind: UnwindAction::Cleanup(_), .. } => {
|
||||
vec!["unwind".into()]
|
||||
}
|
||||
InlineAsm { destination: None, unwind: _, .. } => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum TerminatorEdges<'mir, 'tcx> {
|
||||
/// For terminators that have no successor, like `return`.
|
||||
|
Loading…
Reference in New Issue
Block a user