summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/mir
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/mir')
-rw-r--r--compiler/rustc_middle/src/mir/basic_blocks.rs21
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs522
-rw-r--r--compiler/rustc_middle/src/mir/coverage.rs10
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs91
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs19
-rw-r--r--compiler/rustc_middle/src/mir/interpret/pointer.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs18
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs134
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs1612
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs8
-rw-r--r--compiler/rustc_middle/src/mir/patch.rs27
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs1251
-rw-r--r--compiler/rustc_middle/src/mir/query.rs8
-rw-r--r--compiler/rustc_middle/src/mir/spanview.rs45
-rw-r--r--compiler/rustc_middle/src/mir/statement.rs464
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs118
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs13
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs421
-rw-r--r--compiler/rustc_middle/src/mir/traversal.rs110
-rw-r--r--compiler/rustc_middle/src/mir/type_foldable.rs2
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs58
21 files changed, 2607 insertions, 2347 deletions
diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs
index 0ad17e819..3ecd5b9cd 100644
--- a/compiler/rustc_middle/src/mir/basic_blocks.rs
+++ b/compiler/rustc_middle/src/mir/basic_blocks.rs
@@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph;
use rustc_data_structures::graph::dominators::{dominators, Dominators};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::OnceCell;
+use rustc_data_structures::sync::OnceLock;
use rustc_index::{IndexSlice, IndexVec};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use smallvec::SmallVec;
@@ -23,11 +23,11 @@ pub type SwitchSources = FxHashMap<(BasicBlock, BasicBlock), SmallVec<[Option<u1
#[derive(Clone, Default, Debug)]
struct Cache {
- predecessors: OnceCell<Predecessors>,
- switch_sources: OnceCell<SwitchSources>,
- is_cyclic: OnceCell<bool>,
- reverse_postorder: OnceCell<Vec<BasicBlock>>,
- dominators: OnceCell<Dominators<BasicBlock>>,
+ predecessors: OnceLock<Predecessors>,
+ switch_sources: OnceLock<SwitchSources>,
+ is_cyclic: OnceLock<bool>,
+ reverse_postorder: OnceLock<Vec<BasicBlock>>,
+ dominators: OnceLock<Dominators<BasicBlock>>,
}
impl<'tcx> BasicBlocks<'tcx> {
@@ -63,11 +63,14 @@ impl<'tcx> BasicBlocks<'tcx> {
}
/// Returns basic blocks in a reverse postorder.
+ ///
+ /// See [`traversal::reverse_postorder`]'s docs to learn what is preorder traversal.
+ ///
+ /// [`traversal::reverse_postorder`]: crate::mir::traversal::reverse_postorder
#[inline]
pub fn reverse_postorder(&self) -> &[BasicBlock] {
self.cache.reverse_postorder.get_or_init(|| {
- let mut rpo: Vec<_> =
- Postorder::new(&self.basic_blocks, START_BLOCK).map(|(bb, _)| bb).collect();
+ let mut rpo: Vec<_> = Postorder::new(&self.basic_blocks, START_BLOCK).collect();
rpo.reverse();
rpo
})
@@ -178,7 +181,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> {
}
}
-TrivialTypeTraversalAndLiftImpls! { Cache }
+TrivialTypeTraversalImpls! { Cache }
impl<S: Encoder> Encodable<S> for Cache {
#[inline]
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
new file mode 100644
index 000000000..7c8a57b84
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -0,0 +1,522 @@
+use std::fmt::{self, Debug, Display, Formatter};
+
+use rustc_hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{self as hir};
+use rustc_span::Span;
+use rustc_target::abi::{HasDataLayout, Size};
+
+use crate::mir::interpret::{alloc_range, AllocId, ConstAllocation, ErrorHandled, Scalar};
+use crate::mir::{pretty_print_const_value, Promoted};
+use crate::ty::ScalarInt;
+use crate::ty::{self, print::pretty_print_const, List, Ty, TyCtxt};
+use crate::ty::{GenericArgs, GenericArgsRef};
+
+///////////////////////////////////////////////////////////////////////////
+/// Evaluated Constants
+
+/// Represents the result of const evaluation via the `eval_to_allocation` query.
+/// Not to be confused with `ConstAllocation`, which directly refers to the underlying data!
+/// Here we indirect via an `AllocId`.
+#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
+pub struct ConstAlloc<'tcx> {
+ /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
+ /// (so you can use `AllocMap::unwrap_memory`).
+ pub alloc_id: AllocId,
+ pub ty: Ty<'tcx>,
+}
+
+/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
+/// array length computations, enum discriminants and the pattern matching logic.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
+#[derive(HashStable, Lift)]
+pub enum ConstValue<'tcx> {
+ /// Used for types with `layout::abi::Scalar` ABI.
+ ///
+ /// Not using the enum `Value` to encode that this must not be `Uninit`.
+ Scalar(Scalar),
+
+ /// Only for ZSTs.
+ ZeroSized,
+
+ /// Used for references to unsized types with slice tail.
+ ///
+ /// This is worth an optimized representation since Rust has literals of type `&str` and
+ /// `&[u8]`. Not having to indirect those through an `AllocId` (or two, if we used `Indirect`)
+ /// has shown measurable performance improvements on stress tests. We then reuse this
+ /// optimization for slice-tail types more generally during valtree-to-constval conversion.
+ Slice {
+ /// The allocation storing the slice contents.
+ /// This always points to the beginning of the allocation.
+ data: ConstAllocation<'tcx>,
+ /// The metadata field of the reference.
+ /// This is a "target usize", so we use `u64` as in the interpreter.
+ meta: u64,
+ },
+
+ /// A value not representable by the other variants; needs to be stored in-memory.
+ ///
+ /// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
+ Indirect {
+ /// The backing memory of the value. May contain more memory than needed for just the value
+ /// if this points into some other larger ConstValue.
+ ///
+ /// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
+ /// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
+ /// back, we can preserve the original `AllocId`.
+ alloc_id: AllocId,
+ /// Offset into `alloc`
+ offset: Size,
+ },
+}
+
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+static_assert_size!(ConstValue<'_>, 24);
+
+impl<'tcx> ConstValue<'tcx> {
+ #[inline]
+ pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
+ match *self {
+ ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
+ ConstValue::Scalar(val) => Some(val),
+ }
+ }
+
+ pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
+ self.try_to_scalar()?.try_to_int().ok()
+ }
+
+ pub fn try_to_bits(&self, size: Size) -> Option<u128> {
+ self.try_to_scalar_int()?.to_bits(size).ok()
+ }
+
+ pub fn try_to_bool(&self) -> Option<bool> {
+ self.try_to_scalar_int()?.try_into().ok()
+ }
+
+ pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
+ self.try_to_scalar_int()?.try_to_target_usize(tcx).ok()
+ }
+
+ pub fn try_to_bits_for_ty(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Option<u128> {
+ let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
+ self.try_to_bits(size)
+ }
+
+ pub fn from_bool(b: bool) -> Self {
+ ConstValue::Scalar(Scalar::from_bool(b))
+ }
+
+ pub fn from_u64(i: u64) -> Self {
+ ConstValue::Scalar(Scalar::from_u64(i))
+ }
+
+ pub fn from_u128(i: u128) -> Self {
+ ConstValue::Scalar(Scalar::from_u128(i))
+ }
+
+ pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
+ ConstValue::Scalar(Scalar::from_target_usize(i, cx))
+ }
+
+ /// Must only be called on constants of type `&str` or `&[u8]`!
+ pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
+ let (data, start, end) = match self {
+ ConstValue::Scalar(_) | ConstValue::ZeroSized => {
+ bug!("`try_get_slice_bytes` on non-slice constant")
+ }
+ &ConstValue::Slice { data, meta } => (data, 0, meta),
+ &ConstValue::Indirect { alloc_id, offset } => {
+ // The reference itself is stored behind an indirection.
+ // Load the reference, and then load the actual slice contents.
+ let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
+ let ptr_size = tcx.data_layout.pointer_size;
+ if a.size() < offset + 2 * ptr_size {
+ // (partially) dangling reference
+ return None;
+ }
+ // Read the wide pointer components.
+ let ptr = a
+ .read_scalar(
+ &tcx,
+ alloc_range(offset, ptr_size),
+ /* read_provenance */ true,
+ )
+ .ok()?;
+ let ptr = ptr.to_pointer(&tcx).ok()?;
+ let len = a
+ .read_scalar(
+ &tcx,
+ alloc_range(offset + ptr_size, ptr_size),
+ /* read_provenance */ false,
+ )
+ .ok()?;
+ let len = len.to_target_usize(&tcx).ok()?;
+ if len == 0 {
+ return Some(&[]);
+ }
+ // Non-empty slice, must have memory. We know this is a relative pointer.
+ let (inner_alloc_id, offset) = ptr.into_parts();
+ let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
+ (data, offset.bytes(), offset.bytes() + len)
+ }
+ };
+
+ // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
+ let start = start.try_into().unwrap();
+ let end = end.try_into().unwrap();
+ Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// Constants
+
+#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub enum Const<'tcx> {
+ /// This constant came from the type system.
+ ///
+ /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
+ /// this ensures that we consistently produce "clean" values without data in the padding or
+ /// anything like that.
+ Ty(ty::Const<'tcx>),
+
+ /// An unevaluated mir constant which is not part of the type system.
+ ///
+ /// Note that `Ty(ty::ConstKind::Unevaluated)` and this variant are *not* identical! `Ty` will
+ /// always flow through a valtree, so all data not captured in the valtree is lost. This variant
+ /// directly uses the evaluated result of the given constant, including e.g. data stored in
+ /// padding.
+ Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
+
+ /// This constant cannot go back into the type system, as it represents
+ /// something the type system cannot handle (e.g. pointers).
+ Val(ConstValue<'tcx>, Ty<'tcx>),
+}
+
+impl<'tcx> Const<'tcx> {
+ #[inline(always)]
+ pub fn ty(&self) -> Ty<'tcx> {
+ match self {
+ Const::Ty(c) => c.ty(),
+ Const::Val(_, ty) | Const::Unevaluated(_, ty) => *ty,
+ }
+ }
+
+ #[inline]
+ pub fn try_to_scalar(self) -> Option<Scalar> {
+ match self {
+ Const::Ty(c) => match c.kind() {
+ ty::ConstKind::Value(valtree) => match valtree {
+ ty::ValTree::Leaf(scalar_int) => Some(Scalar::Int(scalar_int)),
+ ty::ValTree::Branch(_) => None,
+ },
+ _ => None,
+ },
+ Const::Val(val, _) => val.try_to_scalar(),
+ Const::Unevaluated(..) => None,
+ }
+ }
+
+ #[inline]
+ pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
+ self.try_to_scalar()?.try_to_int().ok()
+ }
+
+ #[inline]
+ pub fn try_to_bits(self, size: Size) -> Option<u128> {
+ self.try_to_scalar_int()?.to_bits(size).ok()
+ }
+
+ #[inline]
+ pub fn try_to_bool(self) -> Option<bool> {
+ self.try_to_scalar_int()?.try_into().ok()
+ }
+
+ #[inline]
+ pub fn eval(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ span: Option<Span>,
+ ) -> Result<ConstValue<'tcx>, ErrorHandled> {
+ match self {
+ Const::Ty(c) => {
+ // We want to consistently have a "clean" value for type system constants (i.e., no
+ // data hidden in the padding), so we always go through a valtree here.
+ let val = c.eval(tcx, param_env, span)?;
+ Ok(tcx.valtree_to_const_val((self.ty(), val)))
+ }
+ Const::Unevaluated(uneval, _) => {
+ // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
+ tcx.const_eval_resolve(param_env, uneval, span)
+ }
+ Const::Val(val, _) => Ok(val),
+ }
+ }
+
+ /// Normalizes the constant to a value or an error if possible.
+ #[inline]
+ pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
+ match self.eval(tcx, param_env, None) {
+ Ok(val) => Self::Val(val, self.ty()),
+ Err(ErrorHandled::Reported(guar, _span)) => {
+ Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty()))
+ }
+ Err(ErrorHandled::TooGeneric(_span)) => self,
+ }
+ }
+
+ #[inline]
+ pub fn try_eval_scalar(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Option<Scalar> {
+ self.eval(tcx, param_env, None).ok()?.try_to_scalar()
+ }
+
+ #[inline]
+ pub fn try_eval_scalar_int(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Option<ScalarInt> {
+ self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
+ }
+
+ #[inline]
+ pub fn try_eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<u128> {
+ let int = self.try_eval_scalar_int(tcx, param_env)?;
+ let size =
+ tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size;
+ int.to_bits(size).ok()
+ }
+
+ /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
+ #[inline]
+ pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 {
+ self.try_eval_bits(tcx, param_env)
+ .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", self.ty(), self))
+ }
+
+ #[inline]
+ pub fn try_eval_target_usize(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Option<u64> {
+ self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
+ }
+
+ #[inline]
+ /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
+ pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u64 {
+ self.try_eval_target_usize(tcx, param_env)
+ .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
+ }
+
+ #[inline]
+ pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
+ self.try_eval_scalar_int(tcx, param_env)?.try_into().ok()
+ }
+
+ #[inline]
+ pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
+ Self::Val(val, ty)
+ }
+
+ pub fn from_bits(
+ tcx: TyCtxt<'tcx>,
+ bits: u128,
+ param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+ ) -> Self {
+ let size = tcx
+ .layout_of(param_env_ty)
+ .unwrap_or_else(|e| {
+ bug!("could not compute layout for {:?}: {:?}", param_env_ty.value, e)
+ })
+ .size;
+ let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
+
+ Self::Val(cv, param_env_ty.value)
+ }
+
+ #[inline]
+ pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
+ let cv = ConstValue::from_bool(v);
+ Self::Val(cv, tcx.types.bool)
+ }
+
+ #[inline]
+ pub fn zero_sized(ty: Ty<'tcx>) -> Self {
+ let cv = ConstValue::ZeroSized;
+ Self::Val(cv, ty)
+ }
+
+ pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
+ let ty = tcx.types.usize;
+ Self::from_bits(tcx, n as u128, ty::ParamEnv::empty().and(ty))
+ }
+
+ #[inline]
+ pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
+ let val = ConstValue::Scalar(s);
+ Self::Val(val, ty)
+ }
+
+ /// Literals are converted to `Const::Val`, const generic parameters are eagerly
+ /// converted to a constant, everything else becomes `Unevaluated`.
+ #[instrument(skip(tcx), level = "debug", ret)]
+ pub fn from_anon_const(
+ tcx: TyCtxt<'tcx>,
+ def: LocalDefId,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Self {
+ let body_id = match tcx.hir().get_by_def_id(def) {
+ hir::Node::AnonConst(ac) => ac.body,
+ _ => {
+ span_bug!(tcx.def_span(def), "from_anon_const can only process anonymous constants")
+ }
+ };
+
+ let expr = &tcx.hir().body(body_id).value;
+ debug!(?expr);
+
+ // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
+ // currently have to be wrapped in curly brackets, so it's necessary to special-case.
+ let expr = match &expr.kind {
+ hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
+ block.expr.as_ref().unwrap()
+ }
+ _ => expr,
+ };
+ debug!("expr.kind: {:?}", expr.kind);
+
+ let ty = tcx.type_of(def).instantiate_identity();
+ debug!(?ty);
+
+ // FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
+ // does not provide the parents generics to anonymous constants. We still allow generic const
+ // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
+ // ever try to substitute the generic parameters in their bodies.
+ //
+ // While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
+ // cause issues if we were to remove that special-case and try to evaluate the constant instead.
+ use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
+ match expr.kind {
+ ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
+ // Find the name and index of the const parameter by indexing the generics of
+ // the parent item and construct a `ParamConst`.
+ let item_def_id = tcx.parent(def_id);
+ let generics = tcx.generics_of(item_def_id);
+ let index = generics.param_def_id_to_index[&def_id];
+ let name = tcx.item_name(def_id);
+ let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
+ debug!(?ty_const);
+
+ return Self::Ty(ty_const);
+ }
+ _ => {}
+ }
+
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def);
+ let parent_args = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id)
+ && let Some(parent_did) = parent_hir_id.as_owner()
+ {
+ GenericArgs::identity_for_item(tcx, parent_did)
+ } else {
+ List::empty()
+ };
+ debug!(?parent_args);
+
+ let did = def.to_def_id();
+ let child_args = GenericArgs::identity_for_item(tcx, did);
+ let args = tcx.mk_args_from_iter(parent_args.into_iter().chain(child_args.into_iter()));
+ debug!(?args);
+
+ let span = tcx.def_span(def);
+ let uneval = UnevaluatedConst::new(did, args);
+ debug!(?span, ?param_env);
+
+ match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
+ Ok(val) => {
+ debug!("evaluated const value");
+ Self::Val(val, ty)
+ }
+ Err(_) => {
+ debug!("error encountered during evaluation");
+ // Error was handled in `const_eval_resolve`. Here we just create a
+ // new unevaluated const and error hard later in codegen
+ Self::Unevaluated(
+ UnevaluatedConst {
+ def: did,
+ args: GenericArgs::identity_for_item(tcx, did),
+ promoted: None,
+ },
+ ty,
+ )
+ }
+ }
+ }
+
+ pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+ match c.kind() {
+ ty::ConstKind::Value(valtree) => {
+ // Make sure that if `c` is normalized, then the return value is normalized.
+ let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
+ Self::Val(const_val, c.ty())
+ }
+ _ => Self::Ty(c),
+ }
+ }
+}
+
+/// An unevaluated (potentially generic) constant used in MIR.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
+pub struct UnevaluatedConst<'tcx> {
+ pub def: DefId,
+ pub args: GenericArgsRef<'tcx>,
+ pub promoted: Option<Promoted>,
+}
+
+impl<'tcx> UnevaluatedConst<'tcx> {
+ #[inline]
+ pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
+ assert_eq!(self.promoted, None);
+ ty::UnevaluatedConst { def: self.def, args: self.args }
+ }
+}
+
+impl<'tcx> UnevaluatedConst<'tcx> {
+ #[inline]
+ pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
+ UnevaluatedConst { def, args, promoted: Default::default() }
+ }
+
+ #[inline]
+ pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
+ UnevaluatedConst::new(instance.def_id(), instance.args)
+ }
+}
+
+impl<'tcx> Display for Const<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ match *self {
+ Const::Ty(c) => pretty_print_const(c, fmt, true),
+ Const::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
+ // FIXME(valtrees): Correctly print mir constants.
+ Const::Unevaluated(..) => {
+ fmt.write_str("_")?;
+ Ok(())
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index 1efb54bdb..9ef673922 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -45,16 +45,6 @@ impl ExpressionId {
}
}
-rustc_index::newtype_index! {
- /// MappedExpressionIndex values ascend from zero, and are recalculated indexes based on their
- /// array position in the LLVM coverage map "Expressions" array, which is assembled during the
- /// "mapgen" process. They cannot be computed algorithmically, from the other `newtype_index`s.
- #[derive(HashStable)]
- #[max = 0xFFFF_FFFF]
- #[debug_format = "MappedExpressionIndex({})"]
- pub struct MappedExpressionIndex {}
-}
-
/// Operand of a coverage-counter expression.
///
/// Operands can be a constant zero value, an actual coverage counter, or another
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index e6ef5a41e..bc464aca5 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -1,8 +1,9 @@
-use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
+use super::{AllocId, AllocRange, Pointer, Scalar};
-use crate::mir::interpret::ConstValue;
+use crate::error;
+use crate::mir::{ConstAlloc, ConstValue};
use crate::query::TyCtxtAt;
-use crate::ty::{layout, tls, Ty, ValTree};
+use crate::ty::{layout, tls, Ty, TyCtxt, ValTree};
use rustc_data_structures::sync::Lock;
use rustc_errors::{
@@ -11,7 +12,7 @@ use rustc_errors::{
};
use rustc_macros::HashStable;
use rustc_session::CtfeBacktrace;
-use rustc_span::def_id::DefId;
+use rustc_span::{def_id::DefId, Span, DUMMY_SP};
use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange};
use std::borrow::Cow;
@@ -21,16 +22,51 @@ use std::{any::Any, backtrace::Backtrace, fmt};
pub enum ErrorHandled {
/// Already reported an error for this evaluation, and the compilation is
/// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
- Reported(ReportedErrorInfo),
+ Reported(ReportedErrorInfo, Span),
/// Don't emit an error, the evaluation failed because the MIR was generic
/// and the args didn't fully monomorphize it.
- TooGeneric,
+ TooGeneric(Span),
}
impl From<ErrorGuaranteed> for ErrorHandled {
#[inline]
fn from(error: ErrorGuaranteed) -> ErrorHandled {
- ErrorHandled::Reported(error.into())
+ ErrorHandled::Reported(error.into(), DUMMY_SP)
+ }
+}
+
+impl ErrorHandled {
+ pub fn with_span(self, span: Span) -> Self {
+ match self {
+ ErrorHandled::Reported(err, _span) => ErrorHandled::Reported(err, span),
+ ErrorHandled::TooGeneric(_span) => ErrorHandled::TooGeneric(span),
+ }
+ }
+
+ pub fn emit_err(&self, tcx: TyCtxt<'_>) -> ErrorGuaranteed {
+ match self {
+ &ErrorHandled::Reported(err, span) => {
+ if !err.is_tainted_by_errors && !span.is_dummy() {
+ tcx.sess.emit_err(error::ErroneousConstant { span });
+ }
+ err.error
+ }
+ &ErrorHandled::TooGeneric(span) => tcx.sess.delay_span_bug(
+ span,
+ "encountered TooGeneric error when monomorphic data was expected",
+ ),
+ }
+ }
+
+ pub fn emit_note(&self, tcx: TyCtxt<'_>) {
+ match self {
+ &ErrorHandled::Reported(err, span) => {
+ if !err.is_tainted_by_errors && !span.is_dummy() {
+ tcx.sess.emit_note(error::ErroneousConstant { span });
+ }
+ }
+ &ErrorHandled::TooGeneric(_) => {}
+ }
}
}
@@ -45,12 +81,6 @@ impl ReportedErrorInfo {
pub fn tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo {
ReportedErrorInfo { is_tainted_by_errors: true, error }
}
-
- /// Returns true if evaluation failed because MIR was tainted by errors.
- #[inline]
- pub fn is_tainted_by_errors(self) -> bool {
- self.is_tainted_by_errors
- }
}
impl From<ErrorGuaranteed> for ReportedErrorInfo {
@@ -67,10 +97,12 @@ impl Into<ErrorGuaranteed> for ReportedErrorInfo {
}
}
-TrivialTypeTraversalAndLiftImpls! { ErrorHandled }
+TrivialTypeTraversalImpls! { ErrorHandled }
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
+/// `Ok(None)` indicates the constant was fine, but the valtree couldn't be constructed.
+/// This is needed in `thir::pattern::lower_inline_const`.
pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
pub fn struct_error<'tcx>(
@@ -160,6 +192,16 @@ impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
}
}
+impl From<ErrorHandled> for InterpErrorInfo<'_> {
+ fn from(err: ErrorHandled) -> Self {
+ InterpError::InvalidProgram(match err {
+ ErrorHandled::Reported(r, _span) => InvalidProgramInfo::AlreadyReported(r),
+ ErrorHandled::TooGeneric(_span) => InvalidProgramInfo::TooGeneric,
+ })
+ .into()
+ }
+}
+
impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
fn from(kind: InterpError<'tcx>) -> Self {
InterpErrorInfo(Box::new(InterpErrorInfoInner {
@@ -255,9 +297,16 @@ impl_into_diagnostic_arg_through_debug! {
/// Error information for when the program caused Undefined Behavior.
#[derive(Debug)]
-pub enum UndefinedBehaviorInfo<'a> {
+pub enum UndefinedBehaviorInfo<'tcx> {
/// Free-form case. Only for errors that are never caught! Used by miri
Ub(String),
+ // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
+ // dispatched
+ /// A custom (free-form) fluent-translated error, created by `err_ub_custom!`.
+ Custom(crate::error::CustomSubdiagnostic<'tcx>),
+ /// Validation error.
+ ValidationError(ValidationErrorInfo<'tcx>),
+
/// Unreachable code was executed.
Unreachable,
/// A slice/array index projection went out-of-bounds.
@@ -319,12 +368,10 @@ pub enum UndefinedBehaviorInfo<'a> {
UninhabitedEnumVariantWritten(VariantIdx),
/// An uninhabited enum variant is projected.
UninhabitedEnumVariantRead(VariantIdx),
- /// Validation error.
- ValidationError(ValidationErrorInfo<'a>),
- // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
- // dispatched
- /// A custom (free-form) error, created by `err_ub_custom!`.
- Custom(crate::error::CustomSubdiagnostic<'a>),
+ /// ABI-incompatible argument types.
+ AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
+ /// ABI-incompatible return types.
+ AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
}
#[derive(Debug, Clone, Copy)]
@@ -415,6 +462,8 @@ pub enum UnsupportedOpInfo {
/// Free-form case. Only for errors that are never caught!
// FIXME still use translatable diagnostics
Unsupported(String),
+ /// Unsized local variables.
+ UnsizedLocal,
//
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
//
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 3543158bf..d21f82f04 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -149,7 +149,7 @@ pub use self::error::{
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
};
-pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
+pub use self::value::Scalar;
pub use self::allocation::{
alloc_range, AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation,
@@ -162,7 +162,7 @@ pub use self::pointer::{Pointer, PointerArithmetic, Provenance};
/// - A constant
/// - A static
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, Lift, TypeFoldable, TypeVisitable)]
+#[derive(HashStable, TypeFoldable, TypeVisitable)]
pub struct GlobalId<'tcx> {
/// For a constant or static, the `Instance` of the item itself.
/// For a promoted global, the `Instance` of the function they belong to.
@@ -389,7 +389,7 @@ impl<'s> AllocDecodingSession<'s> {
trace!("creating fn alloc ID");
let instance = ty::Instance::decode(decoder);
trace!("decoded fn alloc instance: {:?}", instance);
- let alloc_id = decoder.interner().create_fn_alloc(instance);
+ let alloc_id = decoder.interner().reserve_and_set_fn_alloc(instance);
alloc_id
}
AllocDiscriminant::VTable => {
@@ -399,7 +399,8 @@ impl<'s> AllocDecodingSession<'s> {
let poly_trait_ref =
<Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder);
trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}");
- let alloc_id = decoder.interner().create_vtable_alloc(ty, poly_trait_ref);
+ let alloc_id =
+ decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref);
alloc_id
}
AllocDiscriminant::Static => {
@@ -407,7 +408,7 @@ impl<'s> AllocDecodingSession<'s> {
trace!("creating extern static alloc ID");
let did = <DefId as Decodable<D>>::decode(decoder);
trace!("decoded static def-ID: {:?}", did);
- let alloc_id = decoder.interner().create_static_alloc(did);
+ let alloc_id = decoder.interner().reserve_and_set_static_alloc(did);
alloc_id
}
}
@@ -544,13 +545,13 @@ impl<'tcx> TyCtxt<'tcx> {
/// Generates an `AllocId` for a static or return a cached one in case this function has been
/// called on the same static before.
- pub fn create_static_alloc(self, static_id: DefId) -> AllocId {
+ pub fn reserve_and_set_static_alloc(self, static_id: DefId) -> AllocId {
self.reserve_and_set_dedup(GlobalAlloc::Static(static_id))
}
/// Generates an `AllocId` for a function. Depending on the function type,
/// this might get deduplicated or assigned a new ID each time.
- pub fn create_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
+ pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
// by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be
// duplicated across crates.
@@ -575,7 +576,7 @@ impl<'tcx> TyCtxt<'tcx> {
}
/// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
- pub fn create_vtable_alloc(
+ pub fn reserve_and_set_vtable_alloc(
self,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
@@ -588,7 +589,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Statics with identical content will still point to the same `Allocation`, i.e.,
/// their data will be deduplicated through `Allocation` interning -- but they
/// are different places in memory and as such need different IDs.
- pub fn create_memory_alloc(self, mem: ConstAllocation<'tcx>) -> AllocId {
+ pub fn reserve_and_set_memory_alloc(self, mem: ConstAllocation<'tcx>) -> AllocId {
let id = self.reserve_alloc_id();
self.set_alloc_id_memory(id, mem);
id
diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs
index 65d049193..1c9ce1cb1 100644
--- a/compiler/rustc_middle/src/mir/interpret/pointer.rs
+++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs
@@ -103,7 +103,7 @@ impl<T: HasDataLayout> PointerArithmetic for T {}
/// mostly opaque; the `Machine` trait extends it with some more operations that also have access to
/// some global state.
/// The `Debug` rendering is used to display bare provenance, and for the default impl of `fmt`.
-pub trait Provenance: Copy + fmt::Debug {
+pub trait Provenance: Copy + fmt::Debug + 'static {
/// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address.
/// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are
/// different from what the Abstract Machine prescribes, so the interpreter must prevent any
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index fc659ce18..fbf6403ea 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -61,8 +61,10 @@ impl<'tcx> TyCtxt<'tcx> {
let cid = GlobalId { instance, promoted: ct.promoted };
self.const_eval_global_id(param_env, cid, span)
}
- Ok(None) => Err(ErrorHandled::TooGeneric),
- Err(err) => Err(ErrorHandled::Reported(err.into())),
+ // For errors during resolution, we deliberately do not point at the usage site of the constant,
+ // since for these errors the place the constant is used shouldn't matter.
+ Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)),
+ Err(err) => Err(ErrorHandled::Reported(err.into(), DUMMY_SP)),
}
}
@@ -117,8 +119,10 @@ impl<'tcx> TyCtxt<'tcx> {
}
})
}
- Ok(None) => Err(ErrorHandled::TooGeneric),
- Err(err) => Err(ErrorHandled::Reported(err.into())),
+ // For errors during resolution, we deliberately do not point at the usage site of the constant,
+ // since for these errors the place the constant is used shouldn't matter.
+ Ok(None) => Err(ErrorHandled::TooGeneric(DUMMY_SP)),
+ Err(err) => Err(ErrorHandled::Reported(err.into(), DUMMY_SP)),
}
}
@@ -143,7 +147,8 @@ impl<'tcx> TyCtxt<'tcx> {
// improve caching of queries.
let inputs = self.erase_regions(param_env.and(cid));
if let Some(span) = span {
- self.at(span).eval_to_const_value_raw(inputs)
+ // The query doesn't know where it is being invoked, so we need to fix the span.
+ self.at(span).eval_to_const_value_raw(inputs).map_err(|e| e.with_span(span))
} else {
self.eval_to_const_value_raw(inputs)
}
@@ -162,7 +167,8 @@ impl<'tcx> TyCtxt<'tcx> {
let inputs = self.erase_regions(param_env.and(cid));
debug!(?inputs);
if let Some(span) = span {
- self.at(span).eval_to_valtree(inputs)
+ // The query doesn't know where it is being invoked, so we need to fix the span.
+ self.at(span).eval_to_valtree(inputs).map_err(|e| e.with_span(span))
} else {
self.eval_to_valtree(inputs)
}
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 5345a6588..0d548f886 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -9,102 +9,9 @@ use rustc_apfloat::{
use rustc_macros::HashStable;
use rustc_target::abi::{HasDataLayout, Size};
-use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
+use crate::ty::ScalarInt;
-use super::{
- AllocId, AllocRange, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
- ScalarSizeMismatch,
-};
-
-/// Represents the result of const evaluation via the `eval_to_allocation` query.
-#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
-pub struct ConstAlloc<'tcx> {
- /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
- /// (so you can use `AllocMap::unwrap_memory`).
- pub alloc_id: AllocId,
- pub ty: Ty<'tcx>,
-}
-
-/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
-/// array length computations, enum discriminants and the pattern matching logic.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
-#[derive(HashStable, Lift)]
-pub enum ConstValue<'tcx> {
- /// Used only for types with `layout::abi::Scalar` ABI.
- ///
- /// Not using the enum `Value` to encode that this must not be `Uninit`.
- Scalar(Scalar),
-
- /// Only used for ZSTs.
- ZeroSized,
-
- /// Used only for `&[u8]` and `&str`
- Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
-
- /// A value not represented/representable by `Scalar` or `Slice`
- ByRef {
- /// The backing memory of the value, may contain more memory than needed for just the value
- /// in order to share `ConstAllocation`s between values
- alloc: ConstAllocation<'tcx>,
- /// Offset into `alloc`
- offset: Size,
- },
-}
-
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(ConstValue<'_>, 32);
-
-impl<'tcx> ConstValue<'tcx> {
- #[inline]
- pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
- match *self {
- ConstValue::ByRef { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
- ConstValue::Scalar(val) => Some(val),
- }
- }
-
- pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
- self.try_to_scalar()?.try_to_int().ok()
- }
-
- pub fn try_to_bits(&self, size: Size) -> Option<u128> {
- self.try_to_scalar_int()?.to_bits(size).ok()
- }
-
- pub fn try_to_bool(&self) -> Option<bool> {
- self.try_to_scalar_int()?.try_into().ok()
- }
-
- pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
- self.try_to_scalar_int()?.try_to_target_usize(tcx).ok()
- }
-
- pub fn try_to_bits_for_ty(
- &self,
- tcx: TyCtxt<'tcx>,
- param_env: ParamEnv<'tcx>,
- ty: Ty<'tcx>,
- ) -> Option<u128> {
- let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
- self.try_to_bits(size)
- }
-
- pub fn from_bool(b: bool) -> Self {
- ConstValue::Scalar(Scalar::from_bool(b))
- }
-
- pub fn from_u64(i: u64) -> Self {
- ConstValue::Scalar(Scalar::from_u64(i))
- }
-
- pub fn from_u128(i: u128) -> Self {
- ConstValue::Scalar(Scalar::from_u128(i))
- }
-
- pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
- ConstValue::Scalar(Scalar::from_target_usize(i, cx))
- }
-}
+use super::{AllocId, InterpResult, Pointer, PointerArithmetic, Provenance, ScalarSizeMismatch};
/// A `Scalar` represents an immediate, primitive value existing outside of a
/// `memory::Allocation`. It is in many ways like a small chunk of an `Allocation`, up to 16 bytes in
@@ -267,6 +174,16 @@ impl<Prov> Scalar<Prov> {
}
#[inline]
+ pub fn from_i8(i: i8) -> Self {
+ Self::from_int(i, Size::from_bits(8))
+ }
+
+ #[inline]
+ pub fn from_i16(i: i16) -> Self {
+ Self::from_int(i, Size::from_bits(16))
+ }
+
+ #[inline]
pub fn from_i32(i: i32) -> Self {
Self::from_int(i, Size::from_bits(32))
}
@@ -494,29 +411,18 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
}
#[inline]
- pub fn to_f32(self) -> InterpResult<'tcx, Single> {
- // Going through `u32` to check size and truncation.
- Ok(Single::from_bits(self.to_u32()?.into()))
+ pub fn to_float<F: Float>(self) -> InterpResult<'tcx, F> {
+ // Going through `to_uint` to check size and truncation.
+ Ok(F::from_bits(self.to_uint(Size::from_bits(F::BITS))?))
}
#[inline]
- pub fn to_f64(self) -> InterpResult<'tcx, Double> {
- // Going through `u64` to check size and truncation.
- Ok(Double::from_bits(self.to_u64()?.into()))
+ pub fn to_f32(self) -> InterpResult<'tcx, Single> {
+ self.to_float()
}
-}
-/// Gets the bytes of a constant slice value.
-pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] {
- if let ConstValue::Slice { data, start, end } = val {
- let len = end - start;
- data.inner()
- .get_bytes_strip_provenance(
- cx,
- AllocRange { start: Size::from_bytes(start), size: Size::from_bytes(len) },
- )
- .unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err))
- } else {
- bug!("expected const slice, but found another const value");
+ #[inline]
+ pub fn to_f64(self) -> InterpResult<'tcx, Double> {
+ self.to_float()
}
}
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 9ef3a1b30..0bb1c66da 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -2,29 +2,29 @@
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
-use crate::mir::interpret::{
- AllocRange, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
-};
+use crate::mir::interpret::{AllocRange, ConstAllocation, ErrorHandled, Scalar};
use crate::mir::visit::MirVisitable;
use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
+use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::visit::TypeVisitableExt;
use crate::ty::{self, List, Ty, TyCtxt};
-use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex};
-use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
+use crate::ty::{AdtDef, InstanceDef, UserTypeAnnotationIndex};
+use crate::ty::{GenericArg, GenericArgsRef};
use rustc_data_structures::captures::Captures;
use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg};
use rustc_hir::def::{CtorKind, Namespace};
-use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::{self, GeneratorKind, ImplicitSelfKind};
use rustc_hir::{self as hir, HirId};
use rustc_session::Session;
-use rustc_target::abi::{FieldIdx, Size, VariantIdx};
+use rustc_target::abi::{FieldIdx, VariantIdx};
use polonius_engine::Atom;
pub use rustc_ast::Mutability;
+use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_index::{Idx, IndexSlice, IndexVec};
@@ -35,7 +35,9 @@ use rustc_span::{Span, DUMMY_SP};
use either::Either;
use std::borrow::Cow;
-use std::fmt::{self, Debug, Display, Formatter, Write};
+use std::cell::RefCell;
+use std::collections::hash_map::Entry;
+use std::fmt::{self, Debug, Formatter};
use std::ops::{Index, IndexMut};
use std::{iter, mem};
@@ -43,6 +45,7 @@ pub use self::query::*;
pub use basic_blocks::BasicBlocks;
mod basic_blocks;
+mod consts;
pub mod coverage;
mod generic_graph;
pub mod generic_graphviz;
@@ -53,11 +56,10 @@ pub mod patch;
pub mod pretty;
mod query;
pub mod spanview;
+mod statement;
mod syntax;
-pub use syntax::*;
pub mod tcx;
-pub mod terminator;
-pub use terminator::*;
+mod terminator;
pub mod traversal;
mod type_foldable;
@@ -68,6 +70,11 @@ pub use self::graphviz::write_mir_graphviz;
pub use self::pretty::{
create_dump_file, display_allocation, dump_enabled, dump_mir, write_mir_pretty, PassWhere,
};
+pub use consts::*;
+use pretty::pretty_print_const_value;
+pub use statement::*;
+pub use syntax::*;
+pub use terminator::*;
/// Types for locals
pub type LocalDecls<'tcx> = IndexSlice<Local, LocalDecl<'tcx>>;
@@ -97,6 +104,36 @@ impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> {
}
}
+thread_local! {
+ static PASS_NAMES: RefCell<FxHashMap<&'static str, &'static str>> = {
+ RefCell::new(FxHashMap::default())
+ };
+}
+
+/// Converts a MIR pass name into a snake case form to match the profiling naming style.
+fn to_profiler_name(type_name: &'static str) -> &'static str {
+ PASS_NAMES.with(|names| match names.borrow_mut().entry(type_name) {
+ Entry::Occupied(e) => *e.get(),
+ Entry::Vacant(e) => {
+ let snake_case: String = type_name
+ .chars()
+ .flat_map(|c| {
+ if c.is_ascii_uppercase() {
+ vec!['_', c.to_ascii_lowercase()]
+ } else if c == '-' {
+ vec!['_']
+ } else {
+ vec![c]
+ }
+ })
+ .collect();
+ let result = &*String::leak(format!("mir_pass{}", snake_case));
+ e.insert(result);
+ result
+ }
+ })
+}
+
/// A streamlined trait that you can implement to create a pass; the
/// pass will be named after the type, and it will consist of a main
/// loop that goes over each available MIR and applies `run_pass`.
@@ -106,6 +143,10 @@ pub trait MirPass<'tcx> {
if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }
}
+ fn profiler_name(&self) -> &'static str {
+ to_profiler_name(self.name())
+ }
+
/// Returns `true` if this pass is enabled with the current combination of compiler flags.
fn is_enabled(&self, _sess: &Session) -> bool {
true
@@ -277,7 +318,7 @@ pub struct Body<'tcx> {
/// Constants that are required to evaluate successfully for this MIR to be well-formed.
/// We hold in this field all the constants we are not able to evaluate yet.
- pub required_consts: Vec<Constant<'tcx>>,
+ pub required_consts: Vec<ConstOperand<'tcx>>,
/// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
///
@@ -527,6 +568,34 @@ impl<'tcx> Body<'tcx> {
pub fn is_custom_mir(&self) -> bool {
self.injection_phase.is_some()
}
+
+ /// *Must* be called once the full substitution for this body is known, to ensure that the body
+ /// is indeed fit for code generation or consumption more generally.
+ ///
+ /// Sadly there's no nice way to represent an "arbitrary normalizer", so we take one for
+ /// constants specifically. (`Option<GenericArgsRef>` could be used for that, but the fact
+ /// that `Instance::args_for_mir_body` is private and instead instance exposes normalization
+ /// functions makes it seem like exposing the generic args is not the intended strategy.)
+ ///
+ /// Also sadly, CTFE doesn't even know whether it runs on MIR that is already polymorphic or still monomorphic,
+ /// so we cannot just immediately ICE on TooGeneric.
+ ///
+ /// Returns Ok(()) if everything went fine, and `Err` if a problem occurred and got reported.
+ pub fn post_mono_checks(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ normalize_const: impl Fn(Const<'tcx>) -> Result<Const<'tcx>, ErrorHandled>,
+ ) -> Result<(), ErrorHandled> {
+ // For now, the only thing we have to check is is to ensure that all the constants used in
+ // the body successfully evaluate.
+ for &const_ in &self.required_consts {
+ let c = normalize_const(const_.const_)?;
+ c.eval(tcx, param_env, Some(const_.span))?;
+ }
+
+ Ok(())
+ }
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)]
@@ -706,7 +775,7 @@ pub enum BindingForm<'tcx> {
RefForGuard,
}
-TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> }
+TrivialTypeTraversalImpls! { BindingForm<'tcx> }
mod binding_form_impl {
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@@ -1027,20 +1096,7 @@ impl<'tcx> LocalDecl<'tcx> {
pub enum VarDebugInfoContents<'tcx> {
/// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
Place(Place<'tcx>),
- Const(Constant<'tcx>),
- /// The user variable's data is split across several fragments,
- /// each described by a `VarDebugInfoFragment`.
- /// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
- /// and LLVM's `DW_OP_LLVM_fragment` for more details on
- /// the underlying debuginfo feature this relies on.
- Composite {
- /// Type of the original user variable.
- /// This cannot contain a union or an enum.
- ty: Ty<'tcx>,
- /// All the parts of the original user variable, which ended
- /// up in disjoint places, due to optimizations.
- fragments: Vec<VarDebugInfoFragment<'tcx>>,
- },
+ Const(ConstOperand<'tcx>),
}
impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
@@ -1048,19 +1104,16 @@ impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
match self {
VarDebugInfoContents::Const(c) => write!(fmt, "{c}"),
VarDebugInfoContents::Place(p) => write!(fmt, "{p:?}"),
- VarDebugInfoContents::Composite { ty, fragments } => {
- write!(fmt, "{ty:?}{{ ")?;
- for f in fragments.iter() {
- write!(fmt, "{f:?}, ")?;
- }
- write!(fmt, "}}")
- }
}
}
}
-#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct VarDebugInfoFragment<'tcx> {
+ /// Type of the original user variable.
+ /// This cannot contain a union or an enum.
+ pub ty: Ty<'tcx>,
+
/// Where in the composite user variable this fragment is,
/// represented as a "projection" into the composite variable.
/// At lower levels, this corresponds to a byte/bit range.
@@ -1071,29 +1124,10 @@ pub struct VarDebugInfoFragment<'tcx> {
// to match on the discriminant, or by using custom type debuginfo
// with non-overlapping variants for the composite variable.
pub projection: Vec<PlaceElem<'tcx>>,
-
- /// Where the data for this fragment can be found.
- /// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
- pub contents: Place<'tcx>,
-}
-
-impl Debug for VarDebugInfoFragment<'_> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- for elem in self.projection.iter() {
- match elem {
- ProjectionElem::Field(field, _) => {
- write!(fmt, ".{:?}", field.index())?;
- }
- _ => bug!("unsupported fragment projection `{:?}`", elem),
- }
- }
-
- write!(fmt, " => {:?}", self.contents)
- }
}
/// Debug information pertaining to a user variable.
-#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct VarDebugInfo<'tcx> {
pub name: Symbol,
@@ -1102,6 +1136,13 @@ pub struct VarDebugInfo<'tcx> {
/// (see `LocalDecl`'s `source_info` field for more details).
pub source_info: SourceInfo,
+ /// The user variable's data is split across several fragments,
+ /// each described by a `VarDebugInfoFragment`.
+ /// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
+ /// and LLVM's `DW_OP_LLVM_fragment` for more details on
+ /// the underlying debuginfo feature this relies on.
+ pub composite: Option<Box<VarDebugInfoFragment<'tcx>>>,
+
/// Where the data for this user variable is to be found.
pub value: VarDebugInfoContents<'tcx>,
@@ -1267,542 +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, ..))
- }
-
- /// Getting a description does not require `O` to be printable, and does not
- /// require allocation.
- /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` separately.
- 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.
- 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()),
- }
- }
-
- 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, Debug, 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 Debug for Place<'_> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- for elem in self.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 { .. } => {}
- }
- }
-
- write!(fmt, "{:?}", self.local)?;
-
- for elem in self.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) => {
- 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
@@ -1881,719 +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) => {
- 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) => 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) => {
- write!(fmt, "ShallowInitBox({place:?}, {ty:?})")
- }
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-/// Constants
-///
-/// Two constants are equal if they are the same constant. Note that
-/// this does not necessarily mean that they are `==` in Rust. In
-/// particular, one must be wary of `NaN`!
-
-#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct Constant<'tcx> {
- pub span: Span,
-
- /// Optional user-given type: for something like
- /// `collect::<Vec<_>>`, this would be present and would
- /// indicate that `Vec<_>` was explicitly specified.
- ///
- /// Needed for NLL to impose user-given type constraints.
- pub user_ty: Option<UserTypeAnnotationIndex>,
-
- pub literal: ConstantKind<'tcx>,
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
-#[derive(Lift, TypeFoldable, TypeVisitable)]
-pub enum ConstantKind<'tcx> {
- /// This constant came from the type system
- Ty(ty::Const<'tcx>),
-
- /// An unevaluated mir constant which is not part of the type system.
- Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
-
- /// This constant cannot go back into the type system, as it represents
- /// something the type system cannot handle (e.g. pointers).
- Val(interpret::ConstValue<'tcx>, Ty<'tcx>),
-}
-
-impl<'tcx> Constant<'tcx> {
- pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
- match self.literal.try_to_scalar() {
- Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) {
- GlobalAlloc::Static(def_id) => {
- assert!(!tcx.is_thread_local_static(def_id));
- Some(def_id)
- }
- _ => None,
- },
- _ => None,
- }
- }
- #[inline]
- pub fn ty(&self) -> Ty<'tcx> {
- self.literal.ty()
- }
-}
-
-impl<'tcx> ConstantKind<'tcx> {
- #[inline(always)]
- pub fn ty(&self) -> Ty<'tcx> {
- match self {
- ConstantKind::Ty(c) => c.ty(),
- ConstantKind::Val(_, ty) | ConstantKind::Unevaluated(_, ty) => *ty,
- }
- }
-
- #[inline]
- pub fn try_to_value(self, tcx: TyCtxt<'tcx>) -> Option<interpret::ConstValue<'tcx>> {
- match self {
- ConstantKind::Ty(c) => match c.kind() {
- ty::ConstKind::Value(valtree) => Some(tcx.valtree_to_const_val((c.ty(), valtree))),
- _ => None,
- },
- ConstantKind::Val(val, _) => Some(val),
- ConstantKind::Unevaluated(..) => None,
- }
- }
-
- #[inline]
- pub fn try_to_scalar(self) -> Option<Scalar> {
- match self {
- ConstantKind::Ty(c) => match c.kind() {
- ty::ConstKind::Value(valtree) => match valtree {
- ty::ValTree::Leaf(scalar_int) => Some(Scalar::Int(scalar_int)),
- ty::ValTree::Branch(_) => None,
- },
- _ => None,
- },
- ConstantKind::Val(val, _) => val.try_to_scalar(),
- ConstantKind::Unevaluated(..) => None,
- }
- }
-
- #[inline]
- pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
- Some(self.try_to_scalar()?.assert_int())
- }
-
- #[inline]
- pub fn try_to_bits(self, size: Size) -> Option<u128> {
- self.try_to_scalar_int()?.to_bits(size).ok()
- }
-
- #[inline]
- pub fn try_to_bool(self) -> Option<bool> {
- self.try_to_scalar_int()?.try_into().ok()
- }
-
- #[inline]
- pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
- match self {
- Self::Ty(c) => {
- if let Some(val) = c.try_eval_for_mir(tcx, param_env) {
- match val {
- Ok(val) => Self::Val(val, c.ty()),
- Err(guar) => Self::Ty(ty::Const::new_error(tcx, guar, self.ty())),
- }
- } else {
- self
- }
- }
- Self::Val(_, _) => self,
- Self::Unevaluated(uneval, ty) => {
- // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
- match tcx.const_eval_resolve(param_env, uneval, None) {
- Ok(val) => Self::Val(val, ty),
- Err(ErrorHandled::TooGeneric) => self,
- Err(ErrorHandled::Reported(guar)) => {
- Self::Ty(ty::Const::new_error(tcx, guar.into(), ty))
- }
- }
- }
- }
- }
-
- /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
- #[inline]
- pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
- self.try_eval_bits(tcx, param_env, ty)
- .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
- }
-
- #[inline]
- pub fn try_eval_bits(
- &self,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ty: Ty<'tcx>,
- ) -> Option<u128> {
- match self {
- Self::Ty(ct) => ct.try_eval_bits(tcx, param_env, ty),
- Self::Val(val, t) => {
- assert_eq!(*t, ty);
- let size =
- tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
- val.try_to_bits(size)
- }
- Self::Unevaluated(uneval, ty) => {
- match tcx.const_eval_resolve(param_env, *uneval, None) {
- Ok(val) => {
- let size = tcx
- .layout_of(param_env.with_reveal_all_normalized(tcx).and(*ty))
- .ok()?
- .size;
- val.try_to_bits(size)
- }
- Err(_) => None,
- }
- }
- }
- }
-
- #[inline]
- pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
- match self {
- Self::Ty(ct) => ct.try_eval_bool(tcx, param_env),
- Self::Val(val, _) => val.try_to_bool(),
- Self::Unevaluated(uneval, _) => {
- match tcx.const_eval_resolve(param_env, *uneval, None) {
- Ok(val) => val.try_to_bool(),
- Err(_) => None,
- }
- }
- }
- }
-
- #[inline]
- pub fn try_eval_target_usize(
- &self,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ) -> Option<u64> {
- match self {
- Self::Ty(ct) => ct.try_eval_target_usize(tcx, param_env),
- Self::Val(val, _) => val.try_to_target_usize(tcx),
- Self::Unevaluated(uneval, _) => {
- match tcx.const_eval_resolve(param_env, *uneval, None) {
- Ok(val) => val.try_to_target_usize(tcx),
- Err(_) => None,
- }
- }
- }
- }
-
- #[inline]
- pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
- Self::Val(val, ty)
- }
-
- pub fn from_bits(
- tcx: TyCtxt<'tcx>,
- bits: u128,
- param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
- ) -> Self {
- let size = tcx
- .layout_of(param_env_ty)
- .unwrap_or_else(|e| {
- bug!("could not compute layout for {:?}: {:?}", param_env_ty.value, e)
- })
- .size;
- let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
-
- Self::Val(cv, param_env_ty.value)
- }
-
- #[inline]
- pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
- let cv = ConstValue::from_bool(v);
- Self::Val(cv, tcx.types.bool)
- }
-
- #[inline]
- pub fn zero_sized(ty: Ty<'tcx>) -> Self {
- let cv = ConstValue::ZeroSized;
- Self::Val(cv, ty)
- }
-
- pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
- let ty = tcx.types.usize;
- Self::from_bits(tcx, n as u128, ty::ParamEnv::empty().and(ty))
- }
-
- #[inline]
- pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
- let val = ConstValue::Scalar(s);
- Self::Val(val, ty)
- }
-
- /// Literals are converted to `ConstantKindVal`, const generic parameters are eagerly
- /// converted to a constant, everything else becomes `Unevaluated`.
- #[instrument(skip(tcx), level = "debug", ret)]
- pub fn from_anon_const(
- tcx: TyCtxt<'tcx>,
- def: LocalDefId,
- param_env: ty::ParamEnv<'tcx>,
- ) -> Self {
- let body_id = match tcx.hir().get_by_def_id(def) {
- hir::Node::AnonConst(ac) => ac.body,
- _ => {
- span_bug!(tcx.def_span(def), "from_anon_const can only process anonymous constants")
- }
- };
-
- let expr = &tcx.hir().body(body_id).value;
- debug!(?expr);
-
- // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
- // currently have to be wrapped in curly brackets, so it's necessary to special-case.
- let expr = match &expr.kind {
- hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
- block.expr.as_ref().unwrap()
- }
- _ => expr,
- };
- debug!("expr.kind: {:?}", expr.kind);
-
- let ty = tcx.type_of(def).instantiate_identity();
- debug!(?ty);
-
- // FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
- // does not provide the parents generics to anonymous constants. We still allow generic const
- // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
- // ever try to substitute the generic parameters in their bodies.
- //
- // While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
- // cause issues if we were to remove that special-case and try to evaluate the constant instead.
- use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
- match expr.kind {
- ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
- // Find the name and index of the const parameter by indexing the generics of
- // the parent item and construct a `ParamConst`.
- let item_def_id = tcx.parent(def_id);
- let generics = tcx.generics_of(item_def_id);
- let index = generics.param_def_id_to_index[&def_id];
- let name = tcx.item_name(def_id);
- let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
- debug!(?ty_const);
-
- return Self::Ty(ty_const);
- }
- _ => {}
- }
-
- let hir_id = tcx.hir().local_def_id_to_hir_id(def);
- let parent_args = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id)
- && let Some(parent_did) = parent_hir_id.as_owner()
- {
- GenericArgs::identity_for_item(tcx, parent_did)
- } else {
- List::empty()
- };
- debug!(?parent_args);
-
- let did = def.to_def_id();
- let child_args = GenericArgs::identity_for_item(tcx, did);
- let args = tcx.mk_args_from_iter(parent_args.into_iter().chain(child_args.into_iter()));
- debug!(?args);
-
- let span = tcx.def_span(def);
- let uneval = UnevaluatedConst::new(did, args);
- debug!(?span, ?param_env);
-
- match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
- Ok(val) => {
- debug!("evaluated const value");
- Self::Val(val, ty)
- }
- Err(_) => {
- debug!("error encountered during evaluation");
- // Error was handled in `const_eval_resolve`. Here we just create a
- // new unevaluated const and error hard later in codegen
- Self::Unevaluated(
- UnevaluatedConst {
- def: did,
- args: GenericArgs::identity_for_item(tcx, did),
- promoted: None,
- },
- ty,
- )
- }
- }
- }
-
- pub fn from_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
- match c.kind() {
- ty::ConstKind::Value(valtree) => {
- let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
- Self::Val(const_val, c.ty())
- }
- ty::ConstKind::Unevaluated(uv) => Self::Unevaluated(uv.expand(), c.ty()),
- _ => Self::Ty(c),
- }
- }
-}
-
-/// An unevaluated (potentially generic) constant used in MIR.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
-#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
-pub struct UnevaluatedConst<'tcx> {
- pub def: DefId,
- pub args: GenericArgsRef<'tcx>,
- pub promoted: Option<Promoted>,
-}
-
-impl<'tcx> UnevaluatedConst<'tcx> {
- #[inline]
- pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
- assert_eq!(self.promoted, None);
- ty::UnevaluatedConst { def: self.def, args: self.args }
- }
-}
-
-impl<'tcx> UnevaluatedConst<'tcx> {
- #[inline]
- pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
- UnevaluatedConst { def, args, promoted: Default::default() }
- }
-}
-
/// 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:
@@ -2747,220 +1545,6 @@ rustc_index::newtype_index! {
pub struct Promoted {}
}
-impl<'tcx> Debug for Constant<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- write!(fmt, "{self}")
- }
-}
-
-impl<'tcx> Display for Constant<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- match self.ty().kind() {
- ty::FnDef(..) => {}
- _ => write!(fmt, "const ")?,
- }
- Display::fmt(&self.literal, fmt)
- }
-}
-
-impl<'tcx> Display for ConstantKind<'tcx> {
- fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
- match *self {
- ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
- ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
- // FIXME(valtrees): Correctly print mir constants.
- ConstantKind::Unevaluated(..) => {
- fmt.write_str("_")?;
- Ok(())
- }
- }
- }
-}
-
-fn pretty_print_const<'tcx>(
- c: ty::Const<'tcx>,
- fmt: &mut Formatter<'_>,
- print_types: bool,
-) -> fmt::Result {
- use crate::ty::print::PrettyPrinter;
- ty::tls::with(|tcx| {
- let literal = tcx.lift(c).unwrap();
- let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
- cx.print_alloc_ids = true;
- let cx = cx.pretty_print_const(literal, print_types)?;
- fmt.write_str(&cx.into_buffer())?;
- Ok(())
- })
-}
-
-fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
- write!(fmt, "b\"{}\"", byte_str.escape_ascii())
-}
-
-fn comma_sep<'tcx>(
- fmt: &mut Formatter<'_>,
- elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
-) -> fmt::Result {
- let mut first = true;
- for (ct, ty) in elems {
- if !first {
- fmt.write_str(", ")?;
- }
- pretty_print_const_value(ct, ty, fmt)?;
- first = false;
- }
- Ok(())
-}
-
-// FIXME: Move that into `mir/pretty.rs`.
-fn pretty_print_const_value<'tcx>(
- ct: ConstValue<'tcx>,
- ty: Ty<'tcx>,
- fmt: &mut Formatter<'_>,
-) -> fmt::Result {
- use crate::ty::print::PrettyPrinter;
-
- ty::tls::with(|tcx| {
- let ct = tcx.lift(ct).unwrap();
- let ty = tcx.lift(ty).unwrap();
-
- if tcx.sess.verbose() {
- fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
- return Ok(());
- }
-
- let u8_type = tcx.types.u8;
- match (ct, ty.kind()) {
- // Byte/string slices, printed as (byte) string literals.
- (ConstValue::Slice { data, start, end }, ty::Ref(_, inner, _)) => {
- match inner.kind() {
- ty::Slice(t) => {
- if *t == u8_type {
- // The `inspect` here is okay since we checked the bounds, and `u8` carries
- // no provenance (we have an active slice reference here). We don't use
- // this result to affect interpreter execution.
- let byte_str = data
- .inner()
- .inspect_with_uninit_and_ptr_outside_interpreter(start..end);
- pretty_print_byte_str(fmt, byte_str)?;
- return Ok(());
- }
- }
- ty::Str => {
- // The `inspect` here is okay since we checked the bounds, and `str` carries
- // no provenance (we have an active `str` reference here). We don't use this
- // result to affect interpreter execution.
- let slice = data
- .inner()
- .inspect_with_uninit_and_ptr_outside_interpreter(start..end);
- fmt.write_str(&format!("{:?}", String::from_utf8_lossy(slice)))?;
- return Ok(());
- }
- _ => {}
- }
- }
- (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => {
- let n = n.try_to_bits(tcx.data_layout.pointer_size).unwrap();
- // cast is ok because we already checked for pointer size (32 or 64 bit) above
- let range = AllocRange { start: offset, size: Size::from_bytes(n) };
- let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
- fmt.write_str("*")?;
- pretty_print_byte_str(fmt, byte_str)?;
- return Ok(());
- }
- // Aggregates, printed as array/tuple/struct/variant construction syntax.
- //
- // NB: the `has_non_region_param` check ensures that we can use
- // the `destructure_const` query with an empty `ty::ParamEnv` without
- // introducing ICEs (e.g. via `layout_of`) from missing bounds.
- // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
- // to be able to destructure the tuple into `(0u8, *mut T)`
- (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
- let ct = tcx.lift(ct).unwrap();
- let ty = tcx.lift(ty).unwrap();
- if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) {
- let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
- match *ty.kind() {
- ty::Array(..) => {
- fmt.write_str("[")?;
- comma_sep(fmt, fields)?;
- fmt.write_str("]")?;
- }
- ty::Tuple(..) => {
- fmt.write_str("(")?;
- comma_sep(fmt, fields)?;
- if contents.fields.len() == 1 {
- fmt.write_str(",")?;
- }
- fmt.write_str(")")?;
- }
- ty::Adt(def, _) if def.variants().is_empty() => {
- fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
- }
- ty::Adt(def, args) => {
- let variant_idx = contents
- .variant
- .expect("destructed mir constant of adt without variant idx");
- let variant_def = &def.variant(variant_idx);
- let args = tcx.lift(args).unwrap();
- let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
- cx.print_alloc_ids = true;
- let cx = cx.print_value_path(variant_def.def_id, args)?;
- fmt.write_str(&cx.into_buffer())?;
-
- match variant_def.ctor_kind() {
- Some(CtorKind::Const) => {}
- Some(CtorKind::Fn) => {
- fmt.write_str("(")?;
- comma_sep(fmt, fields)?;
- fmt.write_str(")")?;
- }
- None => {
- fmt.write_str(" {{ ")?;
- let mut first = true;
- for (field_def, (ct, ty)) in
- iter::zip(&variant_def.fields, fields)
- {
- if !first {
- fmt.write_str(", ")?;
- }
- write!(fmt, "{}: ", field_def.name)?;
- pretty_print_const_value(ct, ty, fmt)?;
- first = false;
- }
- fmt.write_str(" }}")?;
- }
- }
- }
- _ => unreachable!(),
- }
- return Ok(());
- }
- }
- (ConstValue::Scalar(scalar), _) => {
- let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
- cx.print_alloc_ids = true;
- let ty = tcx.lift(ty).unwrap();
- cx = cx.pretty_print_const_scalar(scalar, ty)?;
- fmt.write_str(&cx.into_buffer())?;
- return Ok(());
- }
- (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
- let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
- cx.print_alloc_ids = true;
- let cx = cx.print_value_path(*d, s)?;
- fmt.write_str(&cx.into_buffer())?;
- return Ok(());
- }
- // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
- // their fields instead of just dumping the memory.
- _ => {}
- }
- // Fall back to debug pretty printing for invalid constants.
- write!(fmt, "{ct:?}: {ty}")
- })
-}
-
/// `Location` represents the position of the start of the statement; or, if
/// `statement_index` equals the number of statements, then the start of the
/// terminator.
@@ -3043,6 +1627,6 @@ mod size_asserts {
static_assert_size!(StatementKind<'_>, 16);
static_assert_size!(Terminator<'_>, 104);
static_assert_size!(TerminatorKind<'_>, 88);
- static_assert_size!(VarDebugInfo<'_>, 80);
+ static_assert_size!(VarDebugInfo<'_>, 88);
// tidy-alphabetical-end
}
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 8fd980d5a..403e80bd3 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -78,9 +78,11 @@ impl<'tcx> MonoItem<'tcx> {
}
}
- pub fn is_generic_fn(&self) -> bool {
- match *self {
- MonoItem::Fn(ref instance) => instance.args.non_erasable_generics().next().is_some(),
+ pub fn is_generic_fn(&self, tcx: TyCtxt<'tcx>) -> bool {
+ match self {
+ MonoItem::Fn(instance) => {
+ instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some()
+ }
MonoItem::Static(..) | MonoItem::GlobalAsm(..) => false,
}
}
diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_middle/src/mir/patch.rs
index c4c3341f8..da486c346 100644
--- a/compiler/rustc_middle/src/mir/patch.rs
+++ b/compiler/rustc_middle/src/mir/patch.rs
@@ -14,7 +14,8 @@ pub struct MirPatch<'tcx> {
resume_block: Option<BasicBlock>,
// Only for unreachable in cleanup path.
unreachable_cleanup_block: Option<BasicBlock>,
- terminate_block: Option<BasicBlock>,
+ // Cached block for UnwindTerminate (with reason)
+ terminate_block: Option<(BasicBlock, UnwindTerminateReason)>,
body_span: Span,
next_local: usize,
}
@@ -35,13 +36,15 @@ impl<'tcx> MirPatch<'tcx> {
for (bb, block) in body.basic_blocks.iter_enumerated() {
// Check if we already have a resume block
- if let TerminatorKind::Resume = block.terminator().kind && block.statements.is_empty() {
+ if matches!(block.terminator().kind, TerminatorKind::UnwindResume)
+ && block.statements.is_empty()
+ {
result.resume_block = Some(bb);
continue;
}
// Check if we already have an unreachable block
- if let TerminatorKind::Unreachable = block.terminator().kind
+ if matches!(block.terminator().kind, TerminatorKind::Unreachable)
&& block.statements.is_empty()
&& block.is_cleanup
{
@@ -50,8 +53,10 @@ impl<'tcx> MirPatch<'tcx> {
}
// Check if we already have a terminate block
- if let TerminatorKind::Terminate = block.terminator().kind && block.statements.is_empty() {
- result.terminate_block = Some(bb);
+ if let TerminatorKind::UnwindTerminate(reason) = block.terminator().kind
+ && block.statements.is_empty()
+ {
+ result.terminate_block = Some((bb, reason));
continue;
}
}
@@ -68,7 +73,7 @@ impl<'tcx> MirPatch<'tcx> {
statements: vec![],
terminator: Some(Terminator {
source_info: SourceInfo::outermost(self.body_span),
- kind: TerminatorKind::Resume,
+ kind: TerminatorKind::UnwindResume,
}),
is_cleanup: true,
});
@@ -93,20 +98,20 @@ impl<'tcx> MirPatch<'tcx> {
bb
}
- pub fn terminate_block(&mut self) -> BasicBlock {
- if let Some(bb) = self.terminate_block {
- return bb;
+ pub fn terminate_block(&mut self, reason: UnwindTerminateReason) -> BasicBlock {
+ if let Some((cached_bb, cached_reason)) = self.terminate_block && reason == cached_reason {
+ return cached_bb;
}
let bb = self.new_block(BasicBlockData {
statements: vec![],
terminator: Some(Terminator {
source_info: SourceInfo::outermost(self.body_span),
- kind: TerminatorKind::Terminate,
+ kind: TerminatorKind::UnwindTerminate(reason),
}),
is_cleanup: true,
});
- self.terminate_block = Some(bb);
+ self.terminate_block = Some((bb, reason));
bb
}
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 773056e8a..f032fd29d 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1,19 +1,19 @@
use std::collections::BTreeSet;
-use std::fmt::Display;
-use std::fmt::Write as _;
+use std::fmt::{self, Debug, Display, Write as _};
use std::fs;
-use std::io::{self, Write};
+use std::io::{self, Write as _};
use std::path::{Path, PathBuf};
use super::graphviz::write_mir_fn_graphviz;
use super::spanview::write_mir_fn_spanview;
use either::Either;
+use rustc_ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_index::Idx;
use rustc_middle::mir::interpret::{
- alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, ConstValue,
- GlobalAlloc, Pointer, Provenance,
+ alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, GlobalAlloc,
+ Pointer, Provenance,
};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
@@ -79,7 +79,7 @@ pub fn dump_mir<'tcx, F>(
body: &Body<'tcx>,
extra_data: F,
) where
- F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+ F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
if !dump_enabled(tcx, pass_name, body.source.def_id()) {
return;
@@ -116,7 +116,7 @@ fn dump_matched_mir_node<'tcx, F>(
body: &Body<'tcx>,
mut extra_data: F,
) where
- F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+ F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
@@ -260,11 +260,14 @@ pub fn create_dump_file<'tcx>(
)
}
+///////////////////////////////////////////////////////////////////////////
+// Whole MIR bodies
+
/// Write out a human-readable textual representation for the given MIR.
pub fn write_mir_pretty<'tcx>(
tcx: TyCtxt<'tcx>,
single: Option<DefId>,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
) -> io::Result<()> {
writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@@ -278,7 +281,7 @@ pub fn write_mir_pretty<'tcx>(
writeln!(w)?;
}
- let render_body = |w: &mut dyn Write, body| -> io::Result<()> {
+ let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
for body in tcx.promoted_mir(def_id) {
@@ -309,10 +312,10 @@ pub fn write_mir_fn<'tcx, F>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
extra_data: &mut F,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
) -> io::Result<()>
where
- F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+ F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
write_mir_intro(tcx, body, w)?;
for block in body.basic_blocks.indices() {
@@ -330,16 +333,267 @@ where
Ok(())
}
+/// Prints local variables in a scope tree.
+fn write_scope_tree(
+ tcx: TyCtxt<'_>,
+ body: &Body<'_>,
+ scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
+ w: &mut dyn io::Write,
+ parent: SourceScope,
+ depth: usize,
+) -> io::Result<()> {
+ let indent = depth * INDENT.len();
+
+ // Local variable debuginfo.
+ for var_debug_info in &body.var_debug_info {
+ if var_debug_info.source_info.scope != parent {
+ // Not declared in this scope.
+ continue;
+ }
+
+ let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
+
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ writeln!(
+ w,
+ "{0:1$} // in {2}",
+ indented_debug_info,
+ ALIGN,
+ comment(tcx, var_debug_info.source_info),
+ )?;
+ } else {
+ writeln!(w, "{indented_debug_info}")?;
+ }
+ }
+
+ // Local variable types.
+ for (local, local_decl) in body.local_decls.iter_enumerated() {
+ if (1..body.arg_count + 1).contains(&local.index()) {
+ // Skip over argument locals, they're printed in the signature.
+ continue;
+ }
+
+ if local_decl.source_info.scope != parent {
+ // Not declared in this scope.
+ continue;
+ }
+
+ let mut_str = local_decl.mutability.prefix_str();
+
+ let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(
+ "{0:1$}let {2}{3:?}: {4}",
+ INDENT, indent, mut_str, local, local_decl.ty
+ ));
+ if let Some(user_ty) = &local_decl.user_ty {
+ for user_ty in user_ty.projections() {
+ write!(indented_decl, " as {user_ty:?}").unwrap();
+ }
+ }
+ indented_decl.push(';');
+
+ let local_name = if local == RETURN_PLACE { " return place" } else { "" };
+
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ writeln!(
+ w,
+ "{0:1$} //{2} in {3}",
+ indented_decl,
+ ALIGN,
+ local_name,
+ comment(tcx, local_decl.source_info),
+ )?;
+ } else {
+ writeln!(w, "{indented_decl}",)?;
+ }
+ }
+
+ let Some(children) = scope_tree.get(&parent) else {
+ return Ok(());
+ };
+
+ for &child in children {
+ let child_data = &body.source_scopes[child];
+ assert_eq!(child_data.parent_scope, Some(parent));
+
+ let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
+ (
+ format!(
+ " (inlined {}{})",
+ if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
+ callee
+ ),
+ Some(callsite_span),
+ )
+ } else {
+ (String::new(), None)
+ };
+
+ let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
+
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ if let Some(span) = span {
+ writeln!(
+ w,
+ "{0:1$} // at {2}",
+ indented_header,
+ ALIGN,
+ tcx.sess.source_map().span_to_embeddable_string(span),
+ )?;
+ } else {
+ writeln!(w, "{indented_header}")?;
+ }
+ } else {
+ writeln!(w, "{indented_header}")?;
+ }
+
+ write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
+ writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
+ }
+
+ Ok(())
+}
+
+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)
+ }
+}
+
+/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
+/// local variables (both user-defined bindings and compiler temporaries).
+pub fn write_mir_intro<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'_>,
+ w: &mut dyn io::Write,
+) -> io::Result<()> {
+ write_mir_sig(tcx, body, w)?;
+ writeln!(w, "{{")?;
+
+ // construct a scope tree and write it out
+ let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
+ for (index, scope_data) in body.source_scopes.iter().enumerate() {
+ if let Some(parent) = scope_data.parent_scope {
+ scope_tree.entry(parent).or_default().push(SourceScope::new(index));
+ } else {
+ // Only the argument scope has no parent, because it's the root.
+ assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
+ }
+ }
+
+ write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
+
+ // Add an empty line before the first block is printed.
+ writeln!(w)?;
+
+ Ok(())
+}
+
+fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {
+ use rustc_hir::def::DefKind;
+
+ trace!("write_mir_sig: {:?}", body.source.instance);
+ let def_id = body.source.def_id();
+ let kind = tcx.def_kind(def_id);
+ let is_function = match kind {
+ DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
+ _ => tcx.is_closure(def_id),
+ };
+ match (kind, body.source.promoted) {
+ (_, Some(i)) => write!(w, "{i:?} in ")?,
+ (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
+ (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
+ (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
+ (_, _) if is_function => write!(w, "fn ")?,
+ (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
+ _ => bug!("Unexpected def kind {:?}", kind),
+ }
+
+ ty::print::with_forced_impl_filename_line! {
+ // see notes on #41697 elsewhere
+ write!(w, "{}", tcx.def_path_str(def_id))?
+ }
+
+ if body.source.promoted.is_none() && is_function {
+ write!(w, "(")?;
+
+ // fn argument types.
+ for (i, arg) in body.args_iter().enumerate() {
+ if i != 0 {
+ write!(w, ", ")?;
+ }
+ write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
+ }
+
+ write!(w, ") -> {}", body.return_ty())?;
+ } else {
+ assert_eq!(body.arg_count, 0);
+ write!(w, ": {} =", body.return_ty())?;
+ }
+
+ if let Some(yield_ty) = body.yield_ty() {
+ writeln!(w)?;
+ writeln!(w, "yields {yield_ty}")?;
+ }
+
+ write!(w, " ")?;
+ // Next thing that gets printed is the opening {
+
+ Ok(())
+}
+
+fn write_user_type_annotations(
+ tcx: TyCtxt<'_>,
+ body: &Body<'_>,
+ w: &mut dyn io::Write,
+) -> io::Result<()> {
+ if !body.user_type_annotations.is_empty() {
+ writeln!(w, "| User Type Annotations")?;
+ }
+ for (index, annotation) in body.user_type_annotations.iter_enumerated() {
+ writeln!(
+ w,
+ "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
+ index.index(),
+ annotation.user_ty,
+ tcx.sess.source_map().span_to_embeddable_string(annotation.span),
+ with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
+ )?;
+ }
+ if !body.user_type_annotations.is_empty() {
+ writeln!(w, "|")?;
+ }
+ Ok(())
+}
+
+pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
+ if let Some(i) = single {
+ vec![i]
+ } else {
+ tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Basic blocks and their parts (statements, terminators, ...)
+
/// Write out a human-readable textual representation for the given basic block.
pub fn write_basic_block<'tcx, F>(
tcx: TyCtxt<'tcx>,
block: BasicBlock,
body: &Body<'tcx>,
extra_data: &mut F,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
) -> io::Result<()>
where
- F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+ F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
let data = &body[block];
@@ -400,10 +654,528 @@ where
writeln!(w, "{INDENT}}}")
}
+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 Display for NonDivergingIntrinsic<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Assume(op) => write!(f, "assume({op:?})"),
+ Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => {
+ write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})")
+ }
+ }
+ }
+}
+
+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: fmt::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![],
+ }
+ }
+}
+
+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::Fake => "fake ",
+ 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})"))
+ }
+ }
+ }
+}
+
+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> Debug for ConstOperand<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ write!(fmt, "{self}")
+ }
+}
+
+impl<'tcx> Display for ConstOperand<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ match self.ty().kind() {
+ ty::FnDef(..) => {}
+ _ => write!(fmt, "const ")?,
+ }
+ Display::fmt(&self.const_, fmt)
+ }
+}
+
+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::Subtype(_)
+ | 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::Subtype(ty) => {
+ write!(fmt, " as subtype {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: 0, from_end: true } => {
+ write!(fmt, "[{from:?}:]")?;
+ }
+ ProjectionElem::Subslice { from: 0, to, from_end: true } => {
+ 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(())
+}
+
/// After we print the main statement, we sometimes dump extra
/// information. There's often a lot of little things "nuzzled up" in
/// a statement.
-fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
+fn write_extra<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ write: &mut dyn io::Write,
+ mut visit_op: F,
+) -> io::Result<()>
where
F: FnMut(&mut ExtraComments<'tcx>),
{
@@ -443,10 +1215,10 @@ fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
}
impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
- fn visit_constant(&mut self, constant: &Constant<'tcx>, _location: Location) {
- let Constant { span, user_ty, literal } = constant;
- if use_verbose(literal.ty(), true) {
- self.push("mir::Constant");
+ fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {
+ let ConstOperand { span, user_ty, const_ } = constant;
+ if use_verbose(const_.ty(), true) {
+ self.push("mir::ConstOperand");
self.push(&format!(
"+ span: {}",
self.tcx.sess.source_map().span_to_embeddable_string(*span)
@@ -455,34 +1227,35 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
self.push(&format!("+ user_ty: {user_ty:?}"));
}
- // FIXME: this is a poor version of `pretty_print_const_value`.
- let fmt_val = |val: &ConstValue<'tcx>| match val {
- ConstValue::ZeroSized => "<ZST>".to_string(),
- ConstValue::Scalar(s) => format!("Scalar({s:?})"),
- ConstValue::Slice { .. } => "Slice(..)".to_string(),
- ConstValue::ByRef { .. } => "ByRef(..)".to_string(),
+ let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| {
+ let tcx = self.tcx;
+ rustc_data_structures::make_display(move |fmt| {
+ pretty_print_const_value_tcx(tcx, val, ty, fmt)
+ })
};
+ // FIXME: call pretty_print_const_valtree?
let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
- ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({leaf:?})"),
- ty::ValTree::Branch(_) => "ValTree::Branch(..)".to_string(),
+ ty::ValTree::Leaf(leaf) => format!("Leaf({leaf:?})"),
+ ty::ValTree::Branch(_) => "Branch(..)".to_string(),
};
- let val = match literal {
- ConstantKind::Ty(ct) => match ct.kind() {
- ty::ConstKind::Param(p) => format!("Param({p})"),
+ let val = match const_ {
+ Const::Ty(ct) => match ct.kind() {
+ ty::ConstKind::Param(p) => format!("ty::Param({p})"),
ty::ConstKind::Unevaluated(uv) => {
- format!("Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
+ format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
}
- ty::ConstKind::Value(val) => format!("Value({})", fmt_valtree(&val)),
+ ty::ConstKind::Value(val) => format!("ty::Valtree({})", fmt_valtree(&val)),
+ // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
ty::ConstKind::Error(_) => "Error".to_string(),
// These variants shouldn't exist in the MIR.
ty::ConstKind::Placeholder(_)
| ty::ConstKind::Infer(_)
| ty::ConstKind::Expr(_)
- | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal),
+ | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", const_),
},
- ConstantKind::Unevaluated(uv, _) => {
+ Const::Unevaluated(uv, _) => {
format!(
"Unevaluated({}, {:?}, {:?})",
self.tcx.def_path_str(uv.def),
@@ -490,16 +1263,13 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
uv.promoted,
)
}
- // To keep the diffs small, we render this like we render `ty::Const::Value`.
- //
- // This changes once `ty::Const::Value` is represented using valtrees.
- ConstantKind::Val(val, _) => format!("Value({})", fmt_val(&val)),
+ Const::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),
};
// This reflects what `Const` looked liked before `val` was renamed
// as `kind`. We print it like this to avoid having to update
// expected output in a lot of tests.
- self.push(&format!("+ literal: Const {{ ty: {}, val: {} }}", literal.ty(), val));
+ self.push(&format!("+ const_: Const {{ ty: {}, val: {} }}", const_.ty(), val));
}
}
@@ -536,162 +1306,15 @@ fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
format!("scope {} at {}", scope.index(), location,)
}
-/// Prints local variables in a scope tree.
-fn write_scope_tree(
- tcx: TyCtxt<'_>,
- body: &Body<'_>,
- scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
- w: &mut dyn Write,
- parent: SourceScope,
- depth: usize,
-) -> io::Result<()> {
- let indent = depth * INDENT.len();
-
- // Local variable debuginfo.
- for var_debug_info in &body.var_debug_info {
- if var_debug_info.source_info.scope != parent {
- // Not declared in this scope.
- continue;
- }
-
- let indented_debug_info = format!(
- "{0:1$}debug {2} => {3:?};",
- INDENT, indent, var_debug_info.name, var_debug_info.value,
- );
-
- if tcx.sess.opts.unstable_opts.mir_include_spans {
- writeln!(
- w,
- "{0:1$} // in {2}",
- indented_debug_info,
- ALIGN,
- comment(tcx, var_debug_info.source_info),
- )?;
- } else {
- writeln!(w, "{indented_debug_info}")?;
- }
- }
-
- // Local variable types.
- for (local, local_decl) in body.local_decls.iter_enumerated() {
- if (1..body.arg_count + 1).contains(&local.index()) {
- // Skip over argument locals, they're printed in the signature.
- continue;
- }
-
- if local_decl.source_info.scope != parent {
- // Not declared in this scope.
- continue;
- }
-
- let mut_str = local_decl.mutability.prefix_str();
-
- let mut indented_decl =
- format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty);
- if let Some(user_ty) = &local_decl.user_ty {
- for user_ty in user_ty.projections() {
- write!(indented_decl, " as {user_ty:?}").unwrap();
- }
- }
- indented_decl.push(';');
-
- let local_name = if local == RETURN_PLACE { " return place" } else { "" };
-
- if tcx.sess.opts.unstable_opts.mir_include_spans {
- writeln!(
- w,
- "{0:1$} //{2} in {3}",
- indented_decl,
- ALIGN,
- local_name,
- comment(tcx, local_decl.source_info),
- )?;
- } else {
- writeln!(w, "{indented_decl}",)?;
- }
- }
-
- let Some(children) = scope_tree.get(&parent) else {
- return Ok(());
- };
-
- for &child in children {
- let child_data = &body.source_scopes[child];
- assert_eq!(child_data.parent_scope, Some(parent));
-
- let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
- (
- format!(
- " (inlined {}{})",
- if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
- callee
- ),
- Some(callsite_span),
- )
- } else {
- (String::new(), None)
- };
-
- let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
-
- if tcx.sess.opts.unstable_opts.mir_include_spans {
- if let Some(span) = span {
- writeln!(
- w,
- "{0:1$} // at {2}",
- indented_header,
- ALIGN,
- tcx.sess.source_map().span_to_embeddable_string(span),
- )?;
- } else {
- writeln!(w, "{indented_header}")?;
- }
- } else {
- writeln!(w, "{indented_header}")?;
- }
-
- write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
- writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
- }
-
- Ok(())
-}
-
-/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
-/// local variables (both user-defined bindings and compiler temporaries).
-pub fn write_mir_intro<'tcx>(
- tcx: TyCtxt<'tcx>,
- body: &Body<'_>,
- w: &mut dyn Write,
-) -> io::Result<()> {
- write_mir_sig(tcx, body, w)?;
- writeln!(w, "{{")?;
-
- // construct a scope tree and write it out
- let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
- for (index, scope_data) in body.source_scopes.iter().enumerate() {
- if let Some(parent) = scope_data.parent_scope {
- scope_tree.entry(parent).or_default().push(SourceScope::new(index));
- } else {
- // Only the argument scope has no parent, because it's the root.
- assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
- }
- }
-
- write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
-
- // Add an empty line before the first block is printed.
- writeln!(w)?;
-
- Ok(())
-}
+///////////////////////////////////////////////////////////////////////////
+// Allocations
/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
/// allocations.
pub fn write_allocations<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'_>,
- w: &mut dyn Write,
+ w: &mut dyn io::Write,
) -> io::Result<()> {
fn alloc_ids_from_alloc(
alloc: ConstAllocation<'_>,
@@ -702,24 +1325,28 @@ pub fn write_allocations<'tcx>(
fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
match val {
ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => {
- Either::Left(Either::Left(std::iter::once(ptr.provenance)))
+ Either::Left(std::iter::once(ptr.provenance))
}
- ConstValue::Scalar(interpret::Scalar::Int { .. }) => {
- Either::Left(Either::Right(std::iter::empty()))
+ ConstValue::Scalar(interpret::Scalar::Int { .. }) => Either::Right(std::iter::empty()),
+ ConstValue::ZeroSized => Either::Right(std::iter::empty()),
+ ConstValue::Slice { .. } => {
+ // `u8`/`str` slices, shouldn't contain pointers that we want to print.
+ Either::Right(std::iter::empty())
}
- ConstValue::ZeroSized => Either::Left(Either::Right(std::iter::empty())),
- ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
- Either::Right(alloc_ids_from_alloc(alloc))
+ ConstValue::Indirect { alloc_id, .. } => {
+ // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
+ // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
+ Either::Left(std::iter::once(alloc_id))
}
}
}
struct CollectAllocIds(BTreeSet<AllocId>);
impl<'tcx> Visitor<'tcx> for CollectAllocIds {
- fn visit_constant(&mut self, c: &Constant<'tcx>, _: Location) {
- match c.literal {
- ConstantKind::Ty(_) | ConstantKind::Unevaluated(..) => {}
- ConstantKind::Val(val, _) => {
+ fn visit_constant(&mut self, c: &ConstOperand<'tcx>, _: Location) {
+ match c.const_ {
+ Const::Ty(_) | Const::Unevaluated(..) => {}
+ Const::Val(val, _) => {
self.0.extend(alloc_ids_from_const_val(val));
}
}
@@ -736,7 +1363,7 @@ pub fn write_allocations<'tcx>(
let mut todo: Vec<_> = seen.iter().copied().collect();
while let Some(id) = todo.pop() {
let mut write_allocation_track_relocs =
- |w: &mut dyn Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
+ |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
// `.rev()` because we are popping them from the back of the `todo` vector.
for id in alloc_ids_from_alloc(alloc).rev() {
if seen.insert(id) {
@@ -997,91 +1624,173 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
Ok(())
}
-fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> {
- use rustc_hir::def::DefKind;
-
- trace!("write_mir_sig: {:?}", body.source.instance);
- let def_id = body.source.def_id();
- let kind = tcx.def_kind(def_id);
- let is_function = match kind {
- DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
- _ => tcx.is_closure(def_id),
- };
- match (kind, body.source.promoted) {
- (_, Some(i)) => write!(w, "{i:?} in ")?,
- (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
- (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
- (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
- (_, _) if is_function => write!(w, "fn ")?,
- (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
- _ => bug!("Unexpected def kind {:?}", kind),
- }
-
- ty::print::with_forced_impl_filename_line! {
- // see notes on #41697 elsewhere
- write!(w, "{}", tcx.def_path_str(def_id))?
- }
+///////////////////////////////////////////////////////////////////////////
+// Constants
- if body.source.promoted.is_none() && is_function {
- write!(w, "(")?;
+fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
+ write!(fmt, "b\"{}\"", byte_str.escape_ascii())
+}
- // fn argument types.
- for (i, arg) in body.args_iter().enumerate() {
- if i != 0 {
- write!(w, ", ")?;
- }
- write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
+fn comma_sep<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ fmt: &mut Formatter<'_>,
+ elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
+) -> fmt::Result {
+ let mut first = true;
+ for (ct, ty) in elems {
+ if !first {
+ fmt.write_str(", ")?;
}
-
- write!(w, ") -> {}", body.return_ty())?;
- } else {
- assert_eq!(body.arg_count, 0);
- write!(w, ": {} =", body.return_ty())?;
+ pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
+ first = false;
}
-
- if let Some(yield_ty) = body.yield_ty() {
- writeln!(w)?;
- writeln!(w, "yields {yield_ty}")?;
- }
-
- write!(w, " ")?;
- // Next thing that gets printed is the opening {
-
Ok(())
}
-fn write_user_type_annotations(
- tcx: TyCtxt<'_>,
- body: &Body<'_>,
- w: &mut dyn Write,
-) -> io::Result<()> {
- if !body.user_type_annotations.is_empty() {
- writeln!(w, "| User Type Annotations")?;
- }
- for (index, annotation) in body.user_type_annotations.iter_enumerated() {
- writeln!(
- w,
- "| {:?}: user_ty: {:?}, span: {}, inferred_ty: {:?}",
- index.index(),
- annotation.user_ty,
- tcx.sess.source_map().span_to_embeddable_string(annotation.span),
- annotation.inferred_ty,
- )?;
+fn pretty_print_const_value_tcx<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ ct: ConstValue<'tcx>,
+ ty: Ty<'tcx>,
+ fmt: &mut Formatter<'_>,
+) -> fmt::Result {
+ use crate::ty::print::PrettyPrinter;
+
+ if tcx.sess.verbose() {
+ fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
+ return Ok(());
}
- if !body.user_type_annotations.is_empty() {
- writeln!(w, "|")?;
+
+ let u8_type = tcx.types.u8;
+ match (ct, ty.kind()) {
+ // Byte/string slices, printed as (byte) string literals.
+ (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
+ if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
+ fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
+ return Ok(());
+ }
+ }
+ (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
+ if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
+ pretty_print_byte_str(fmt, data)?;
+ return Ok(());
+ }
+ }
+ (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
+ let n = n.try_to_target_usize(tcx).unwrap();
+ let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
+ // cast is ok because we already checked for pointer size (32 or 64 bit) above
+ let range = AllocRange { start: offset, size: Size::from_bytes(n) };
+ let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
+ fmt.write_str("*")?;
+ pretty_print_byte_str(fmt, byte_str)?;
+ return Ok(());
+ }
+ // Aggregates, printed as array/tuple/struct/variant construction syntax.
+ //
+ // NB: the `has_non_region_param` check ensures that we can use
+ // the `destructure_const` query with an empty `ty::ParamEnv` without
+ // introducing ICEs (e.g. via `layout_of`) from missing bounds.
+ // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
+ // to be able to destructure the tuple into `(0u8, *mut T)`
+ (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
+ let ct = tcx.lift(ct).unwrap();
+ let ty = tcx.lift(ty).unwrap();
+ if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics(ct, ty) {
+ let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
+ match *ty.kind() {
+ ty::Array(..) => {
+ fmt.write_str("[")?;
+ comma_sep(tcx, fmt, fields)?;
+ fmt.write_str("]")?;
+ }
+ ty::Tuple(..) => {
+ fmt.write_str("(")?;
+ comma_sep(tcx, fmt, fields)?;
+ if contents.fields.len() == 1 {
+ fmt.write_str(",")?;
+ }
+ fmt.write_str(")")?;
+ }
+ ty::Adt(def, _) if def.variants().is_empty() => {
+ fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
+ }
+ ty::Adt(def, args) => {
+ let variant_idx = contents
+ .variant
+ .expect("destructed mir constant of adt without variant idx");
+ let variant_def = &def.variant(variant_idx);
+ let args = tcx.lift(args).unwrap();
+ let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+ cx.print_alloc_ids = true;
+ let cx = cx.print_value_path(variant_def.def_id, args)?;
+ fmt.write_str(&cx.into_buffer())?;
+
+ match variant_def.ctor_kind() {
+ Some(CtorKind::Const) => {}
+ Some(CtorKind::Fn) => {
+ fmt.write_str("(")?;
+ comma_sep(tcx, fmt, fields)?;
+ fmt.write_str(")")?;
+ }
+ None => {
+ fmt.write_str(" {{ ")?;
+ let mut first = true;
+ for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)
+ {
+ if !first {
+ fmt.write_str(", ")?;
+ }
+ write!(fmt, "{}: ", field_def.name)?;
+ pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
+ first = false;
+ }
+ fmt.write_str(" }}")?;
+ }
+ }
+ }
+ _ => unreachable!(),
+ }
+ return Ok(());
+ }
+ }
+ (ConstValue::Scalar(scalar), _) => {
+ let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+ cx.print_alloc_ids = true;
+ let ty = tcx.lift(ty).unwrap();
+ cx = cx.pretty_print_const_scalar(scalar, ty)?;
+ fmt.write_str(&cx.into_buffer())?;
+ return Ok(());
+ }
+ (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
+ let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+ cx.print_alloc_ids = true;
+ let cx = cx.print_value_path(*d, s)?;
+ fmt.write_str(&cx.into_buffer())?;
+ return Ok(());
+ }
+ // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
+ // their fields instead of just dumping the memory.
+ _ => {}
}
- Ok(())
+ // Fall back to debug pretty printing for invalid constants.
+ write!(fmt, "{ct:?}: {ty}")
}
-pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
- if let Some(i) = single {
- vec![i]
- } else {
- tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
- }
+pub(crate) fn pretty_print_const_value<'tcx>(
+ ct: ConstValue<'tcx>,
+ ty: Ty<'tcx>,
+ fmt: &mut Formatter<'_>,
+) -> fmt::Result {
+ ty::tls::with(|tcx| {
+ let ct = tcx.lift(ct).unwrap();
+ let ty = tcx.lift(ty).unwrap();
+ pretty_print_const_value_tcx(tcx, ct, ty, fmt)
+ })
}
+///////////////////////////////////////////////////////////////////////////
+// Miscellaneous
+
/// Calc converted u64 decimal into hex and return it's length in chars
///
/// ```ignore (cannot-test-private-function)
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 71bec49af..c74a9536b 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -1,6 +1,5 @@
//! Values computed by queries that use MIR.
-use crate::mir::interpret::ConstValue;
use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::unord::UnordSet;
@@ -16,7 +15,7 @@ use smallvec::SmallVec;
use std::cell::Cell;
use std::fmt::{self, Debug};
-use super::SourceInfo;
+use super::{ConstValue, SourceInfo};
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
pub enum UnsafetyViolationKind {
@@ -334,7 +333,7 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16);
///
/// See also `rustc_const_eval::borrow_check::constraints`.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
-#[derive(TyEncodable, TyDecodable, HashStable, Lift, TypeVisitable, TypeFoldable)]
+#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)]
pub enum ConstraintCategory<'tcx> {
Return(ReturnConstraint),
Yield,
@@ -415,8 +414,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
pub fn bind(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self {
let inner = tcx.fold_regions(ty, |r, depth| match r.kind() {
ty::ReVar(vid) => {
- let br =
- ty::BoundRegion { var: ty::BoundVar::new(vid.index()), kind: ty::BrAnon(None) };
+ let br = ty::BoundRegion { var: ty::BoundVar::new(vid.index()), kind: ty::BrAnon };
ty::Region::new_late_bound(tcx, depth, br)
}
_ => bug!("unexpected region in ClosureOutlivesSubjectTy: {r:?}"),
diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs
index 20a9e6889..a5358687c 100644
--- a/compiler/rustc_middle/src/mir/spanview.rs
+++ b/compiler/rustc_middle/src/mir/spanview.rs
@@ -238,45 +238,6 @@ pub fn source_range_no_file(tcx: TyCtxt<'_>, span: Span) -> String {
format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1)
}
-pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
- use StatementKind::*;
- match statement.kind {
- Assign(..) => "Assign",
- FakeRead(..) => "FakeRead",
- SetDiscriminant { .. } => "SetDiscriminant",
- Deinit(..) => "Deinit",
- StorageLive(..) => "StorageLive",
- StorageDead(..) => "StorageDead",
- Retag(..) => "Retag",
- PlaceMention(..) => "PlaceMention",
- AscribeUserType(..) => "AscribeUserType",
- Coverage(..) => "Coverage",
- Intrinsic(..) => "Intrinsic",
- ConstEvalCounter => "ConstEvalCounter",
- Nop => "Nop",
- }
-}
-
-pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str {
- use TerminatorKind::*;
- match term.kind {
- Goto { .. } => "Goto",
- SwitchInt { .. } => "SwitchInt",
- Resume => "Resume",
- Terminate => "Terminate",
- Return => "Return",
- Unreachable => "Unreachable",
- Drop { .. } => "Drop",
- Call { .. } => "Call",
- Assert { .. } => "Assert",
- Yield { .. } => "Yield",
- GeneratorDrop => "GeneratorDrop",
- FalseEdge { .. } => "FalseEdge",
- FalseUnwind { .. } => "FalseUnwind",
- InlineAsm { .. } => "InlineAsm",
- }
-}
-
fn statement_span_viewable<'tcx>(
tcx: TyCtxt<'tcx>,
body_span: Span,
@@ -304,7 +265,7 @@ fn terminator_span_viewable<'tcx>(
if !body_span.contains(span) {
return None;
}
- let id = format!("{}:{}", bb.index(), terminator_kind_name(term));
+ let id = format!("{}:{}", bb.index(), term.kind.name());
let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator);
Some(SpanViewable { bb, span, id, tooltip })
}
@@ -631,7 +592,7 @@ fn tooltip<'tcx>(
"\n{}{}: {}: {:?}",
TOOLTIP_INDENT,
source_range,
- statement_kind_name(&statement),
+ statement.kind.name(),
statement
));
}
@@ -641,7 +602,7 @@ fn tooltip<'tcx>(
"\n{}{}: {}: {:?}",
TOOLTIP_INDENT,
source_range,
- terminator_kind_name(term),
+ term.kind.name(),
term.kind
));
}
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
new file mode 100644
index 000000000..3471d620e
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -0,0 +1,464 @@
+/// Functionality for statements, operands, places, and things that appear in them.
+use super::{interpret::GlobalAlloc, *};
+
+///////////////////////////////////////////////////////////////////////////
+// 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::Subtype(_)
+ | 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::Subtype(_)
+ | 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::Subtype(_)
+ | 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(ConstOperand {
+ span,
+ user_ty: None,
+ const_: Const::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(ConstOperand {
+ span,
+ user_ty: None,
+ const_: Const::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 `ConstOperand` that is the target of this `Operand`, or `None` if this `Operand` is a
+ /// place.
+ pub fn constant(&self) -> Option<&ConstOperand<'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()?.const_.ty();
+ if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None }
+ }
+}
+
+impl<'tcx> ConstOperand<'tcx> {
+ pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
+ match self.const_.try_to_scalar() {
+ Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) {
+ GlobalAlloc::Static(def_id) => {
+ assert!(!tcx.is_thread_local_static(def_id));
+ Some(def_id)
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+
+ #[inline]
+ pub fn ty(&self) -> Ty<'tcx> {
+ self.const_.ty()
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// 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::Fake => Mutability::Not,
+ BorrowKind::Mut { .. } => Mutability::Mut,
+ }
+ }
+
+ pub fn allows_two_phase_borrow(&self) -> bool {
+ match *self {
+ BorrowKind::Shared
+ | BorrowKind::Fake
+ | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
+ false
+ }
+ BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
+ }
+ }
+}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index be27bf75d..0b95fdfa1 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -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, Const, 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.
///
@@ -122,7 +123,7 @@ pub enum AnalysisPhase {
/// * [`TerminatorKind::FalseEdge`]
/// * [`StatementKind::FakeRead`]
/// * [`StatementKind::AscribeUserType`]
- /// * [`Rvalue::Ref`] with `BorrowKind::Shallow`
+ /// * [`Rvalue::Ref`] with `BorrowKind::Fake`
///
/// Furthermore, `Deref` projections must be the first projection within any place (if they
/// appear at all)
@@ -138,6 +139,7 @@ pub enum RuntimePhase {
/// * [`TerminatorKind::Yield`]
/// * [`TerminatorKind::GeneratorDrop`]
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
+ /// * [`PlaceElem::OpaqueCast`]
///
/// And the following variants are allowed:
/// * [`StatementKind::Retag`]
@@ -180,7 +182,7 @@ pub enum BorrowKind {
/// should not prevent `if let None = x { ... }`, for example, because the
/// mutating `(*x as Some).0` can't affect the discriminant of `x`.
/// We can also report errors with this kind of borrow differently.
- Shallow,
+ Fake,
/// Data is mutable and not aliasable.
Mut { kind: MutBorrowKind },
@@ -380,6 +382,28 @@ pub enum StatementKind<'tcx> {
Nop,
}
+impl StatementKind<'_> {
+ /// Returns a simple string representation of a `StatementKind` variant, independent of any
+ /// values it might hold (e.g. `StatementKind::Assign` always returns `"Assign"`).
+ pub const fn name(&self) -> &'static str {
+ match self {
+ StatementKind::Assign(..) => "Assign",
+ StatementKind::FakeRead(..) => "FakeRead",
+ StatementKind::SetDiscriminant { .. } => "SetDiscriminant",
+ StatementKind::Deinit(..) => "Deinit",
+ StatementKind::StorageLive(..) => "StorageLive",
+ StatementKind::StorageDead(..) => "StorageDead",
+ StatementKind::Retag(..) => "Retag",
+ StatementKind::PlaceMention(..) => "PlaceMention",
+ StatementKind::AscribeUserType(..) => "AscribeUserType",
+ StatementKind::Coverage(..) => "Coverage",
+ StatementKind::Intrinsic(..) => "Intrinsic",
+ StatementKind::ConstEvalCounter => "ConstEvalCounter",
+ StatementKind::Nop => "Nop",
+ }
+ }
+}
+
#[derive(
Clone,
TyEncodable,
@@ -416,17 +440,6 @@ pub enum NonDivergingIntrinsic<'tcx> {
CopyNonOverlapping(CopyNonOverlapping<'tcx>),
}
-impl std::fmt::Display for NonDivergingIntrinsic<'_> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Assume(op) => write!(f, "assume({op:?})"),
- Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => {
- write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})")
- }
- }
- }
-}
-
/// Describes what kind of retag is to be performed.
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, Hash, HashStable)]
#[rustc_pass_by_value]
@@ -593,13 +606,13 @@ pub enum TerminatorKind<'tcx> {
///
/// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
/// deaggregation runs.
- Resume,
+ UnwindResume,
/// Indicates that the landing pad is finished and that the process should terminate.
///
/// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
/// cleanup blocks.
- Terminate,
+ UnwindTerminate(UnwindTerminateReason),
/// Returns from the function.
///
@@ -790,8 +803,8 @@ impl TerminatorKind<'_> {
match self {
TerminatorKind::Goto { .. } => "Goto",
TerminatorKind::SwitchInt { .. } => "SwitchInt",
- TerminatorKind::Resume => "Resume",
- TerminatorKind::Terminate => "Terminate",
+ TerminatorKind::UnwindResume => "UnwindResume",
+ TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
TerminatorKind::Return => "Return",
TerminatorKind::Unreachable => "Unreachable",
TerminatorKind::Drop { .. } => "Drop",
@@ -806,6 +819,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)]
@@ -820,11 +854,22 @@ pub enum UnwindAction {
/// Terminates the execution if unwind happens.
///
/// Depending on the platform and situation this may cause a non-unwindable panic or abort.
- Terminate,
+ Terminate(UnwindTerminateReason),
/// Cleanups to be done.
Cleanup(BasicBlock),
}
+/// The reason we are terminating the process during unwinding.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub enum UnwindTerminateReason {
+ /// Unwinding is just not possible given the ABI of this function.
+ Abi,
+ /// We were already cleaning up for an ongoing unwind, and a *second*, *nested* unwind was
+ /// triggered by the drop glue.
+ InCleanup,
+}
+
/// Information about an assertion failure.
#[derive(Clone, Hash, HashStable, PartialEq, Debug)]
#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
@@ -858,10 +903,10 @@ pub enum InlineAsmOperand<'tcx> {
out_place: Option<Place<'tcx>>,
},
Const {
- value: Box<Constant<'tcx>>,
+ value: Box<ConstOperand<'tcx>>,
},
SymFn {
- value: Box<Constant<'tcx>>,
+ value: Box<ConstOperand<'tcx>>,
},
SymStatic {
def_id: DefId,
@@ -1030,6 +1075,18 @@ pub enum ProjectionElem<V, T> {
/// Like an explicit cast from an opaque type to a concrete type, but without
/// requiring an intermediate variable.
OpaqueCast(T),
+
+ /// 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.
+ ///
+ /// This goal is achieved with mir_transform pass `Subtyper`, which runs right after
+ /// borrowchecker, as we only care about subtyping that can affect trait selection and
+ /// `TypeId`.
+ Subtype(T),
}
/// Alias for projections as they appear in places, where the base is a place
@@ -1081,7 +1138,22 @@ pub enum Operand<'tcx> {
Move(Place<'tcx>),
/// Constants are already semantically values, and remain unchanged.
- Constant(Box<Constant<'tcx>>),
+ Constant(Box<ConstOperand<'tcx>>),
+}
+
+#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub struct ConstOperand<'tcx> {
+ pub span: Span,
+
+ /// Optional user-given type: for something like
+ /// `collect::<Vec<_>>`, this would be present and would
+ /// indicate that `Vec<_>` was explicitly specified.
+ ///
+ /// Needed for NLL to impose user-given type constraints.
+ pub user_ty: Option<UserTypeAnnotationIndex>,
+
+ pub const_: Const<'tcx>,
}
///////////////////////////////////////////////////////////////////////////
@@ -1274,7 +1346,7 @@ pub enum AggregateKind<'tcx> {
Generator(DefId, GenericArgsRef<'tcx>, hir::Movability),
}
-#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum NullOp<'tcx> {
/// Returns the size of a value of that type
SizeOf,
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index f79697936..7df25fc5c 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -69,7 +69,7 @@ impl<'tcx> PlaceTy<'tcx> {
param_env: ty::ParamEnv<'tcx>,
elem: &ProjectionElem<V, T>,
mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
- mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>,
+ mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
) -> PlaceTy<'tcx>
where
V: ::std::fmt::Debug,
@@ -110,7 +110,12 @@ impl<'tcx> PlaceTy<'tcx> {
PlaceTy { ty: self.ty, variant_index: Some(index) }
}
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
- ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)),
+ ProjectionElem::OpaqueCast(ty) => {
+ PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
+ }
+ ProjectionElem::Subtype(ty) => {
+ PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
+ }
};
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
answer
@@ -227,7 +232,7 @@ impl<'tcx> Operand<'tcx> {
{
match self {
&Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
- Operand::Constant(c) => c.literal.ty(),
+ Operand::Constant(c) => c.const_.ty(),
}
}
}
@@ -273,7 +278,7 @@ impl BorrowKind {
// We have no type corresponding to a shallow borrow, so use
// `&` as an approximation.
- BorrowKind::Shallow => hir::Mutability::Not,
+ BorrowKind::Fake => hir::Mutability::Not,
}
}
}
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 1f878d23b..02aab4a89 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -1,38 +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.
///
@@ -100,6 +78,202 @@ impl<'a> Iterator for SwitchTargetsIter<'a> {
impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
+impl UnwindAction {
+ fn cleanup_block(self) -> Option<BasicBlock> {
+ match self {
+ UnwindAction::Cleanup(bb) => Some(bb),
+ UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate(_) => None,
+ }
+ }
+}
+
+impl UnwindTerminateReason {
+ pub fn as_str(self) -> &'static str {
+ // Keep this in sync with the messages in `core/src/panicking.rs`.
+ match self {
+ UnwindTerminateReason::Abi => "panic in a function that cannot unwind",
+ UnwindTerminateReason::InCleanup => "panic in a destructor during cleanup",
+ }
+ }
+
+ /// A short representation of this used for MIR printing.
+ pub fn as_short_str(self) -> &'static str {
+ match self {
+ UnwindTerminateReason::Abi => "abi",
+ UnwindTerminateReason::InCleanup => "cleanup",
+ }
+ }
+
+ pub fn lang_item(self) -> LangItem {
+ match self {
+ UnwindTerminateReason::Abi => LangItem::PanicCannotUnwind,
+ UnwindTerminateReason::InCleanup => LangItem::PanicInCleanup,
+ }
+ }
+}
+
+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,
@@ -155,8 +329,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| InlineAsm { destination: Some(t), unwind: _, .. } => {
Some(t).into_iter().chain((&[]).into_iter().copied())
}
- Resume
- | Terminate
+ UnwindResume
+ | UnwindTerminate(_)
| GeneratorDrop
| Return
| Unreachable
@@ -197,8 +371,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| InlineAsm { destination: Some(ref mut t), unwind: _, .. } => {
Some(t).into_iter().chain(&mut [])
}
- Resume
- | Terminate
+ UnwindResume
+ | UnwindTerminate(_)
| GeneratorDrop
| Return
| Unreachable
@@ -214,8 +388,8 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn unwind(&self) -> Option<&UnwindAction> {
match *self {
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
@@ -233,8 +407,8 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
match *self {
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
@@ -264,174 +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());
-
- let unwind = match self.unwind() {
- // Not needed or included in successors
- None | Some(UnwindAction::Cleanup(_)) => None,
- Some(UnwindAction::Continue) => Some("unwind continue"),
- Some(UnwindAction::Unreachable) => Some("unwind unreachable"),
- Some(UnwindAction::Terminate) => Some("unwind terminate"),
- };
-
- match (successor_count, unwind) {
- (0, None) => Ok(()),
- (0, Some(unwind)) => write!(fmt, " -> {unwind}"),
- (1, None) => 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 let Some(unwind) = unwind {
- write!(fmt, ", {unwind}")?;
- }
- 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"),
- Resume => write!(fmt, "resume"),
- Terminate => write!(fmt, "abort"),
- 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 | Resume | Terminate | 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`.
@@ -443,7 +449,8 @@ pub enum TerminatorEdges<'mir, 'tcx> {
/// Special action for `Yield`, `Call` and `InlineAsm` terminators.
AssignOnReturn {
return_: Option<BasicBlock>,
- unwind: UnwindAction,
+ /// The cleanup block, if it exists.
+ cleanup: Option<BasicBlock>,
place: CallReturnPlaces<'mir, 'tcx>,
},
/// Special edge for `SwitchInt`.
@@ -486,7 +493,9 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
use TerminatorKind::*;
match *self {
- Return | Resume | Terminate | GeneratorDrop | Unreachable => TerminatorEdges::None,
+ Return | UnwindResume | UnwindTerminate(_) | GeneratorDrop | Unreachable => {
+ TerminatorEdges::None
+ }
Goto { target } => TerminatorEdges::Single(target),
@@ -494,7 +503,7 @@ impl<'tcx> TerminatorKind<'tcx> {
| Drop { target, unwind, place: _, replace: _ }
| FalseUnwind { real_target: target, unwind } => match unwind {
UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
- UnwindAction::Continue | UnwindAction::Terminate | UnwindAction::Unreachable => {
+ UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
TerminatorEdges::Single(target)
}
},
@@ -506,7 +515,7 @@ impl<'tcx> TerminatorKind<'tcx> {
Yield { resume: target, drop, resume_arg, value: _ } => {
TerminatorEdges::AssignOnReturn {
return_: Some(target),
- unwind: drop.map_or(UnwindAction::Terminate, UnwindAction::Cleanup),
+ cleanup: drop,
place: CallReturnPlaces::Yield(resume_arg),
}
}
@@ -514,7 +523,7 @@ impl<'tcx> TerminatorKind<'tcx> {
Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => {
TerminatorEdges::AssignOnReturn {
return_: target,
- unwind,
+ cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::Call(destination),
}
}
@@ -528,7 +537,7 @@ impl<'tcx> TerminatorKind<'tcx> {
unwind,
} => TerminatorEdges::AssignOnReturn {
return_: destination,
- unwind,
+ cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::InlineAsm(operands),
},
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index ec16a8470..a1ff8410e 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -41,6 +41,12 @@ impl<'a, 'tcx> Preorder<'a, 'tcx> {
}
}
+/// Preorder traversal of a graph.
+///
+/// This function creates an iterator over the `Body`'s basic blocks, that
+/// returns basic blocks in a preorder.
+///
+/// See [`Preorder`]'s docs to learn what is preorder traversal.
pub fn preorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Preorder<'a, 'tcx> {
Preorder::new(body, START_BLOCK)
}
@@ -178,7 +184,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
// When we yield `C` and call `traverse_successor`, we push `B` to the stack, but
// since we've already visited `E`, that child isn't added to the stack. The last
// two iterations yield `B` and finally `A` for a final traversal of [E, D, C, B, A]
- while let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() && let Some(bb) = iter.next_back() {
+ while let Some(bb) = self.visit_stack.last_mut().and_then(|(_, iter)| iter.next_back()) {
if self.visited.insert(bb) {
if let Some(term) = &self.basic_blocks[bb].terminator {
self.visit_stack.push((bb, term.successors()));
@@ -188,16 +194,14 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
}
}
-impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
- type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+impl<'tcx> Iterator for Postorder<'_, 'tcx> {
+ type Item = BasicBlock;
- fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
- let next = self.visit_stack.pop();
- if next.is_some() {
- self.traverse_successor();
- }
+ fn next(&mut self) -> Option<BasicBlock> {
+ let (bb, _) = self.visit_stack.pop()?;
+ self.traverse_successor();
- next.map(|(bb, _)| (bb, &self.basic_blocks[bb]))
+ Some(bb)
}
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -215,10 +219,14 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
}
}
-/// Creates an iterator over the `Body`'s basic blocks, that:
+/// Postorder traversal of a graph.
+///
+/// This function creates an iterator over the `Body`'s basic blocks, that:
/// - returns basic blocks in a postorder,
/// - traverses the `BasicBlocks` CFG cache's reverse postorder backwards, and does not cache the
/// postorder itself.
+///
+/// See [`Postorder`]'s docs to learn what is postorder traversal.
pub fn postorder<'a, 'tcx>(
body: &'a Body<'tcx>,
) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
@@ -226,7 +234,28 @@ pub fn postorder<'a, 'tcx>(
reverse_postorder(body).rev()
}
-/// Reverse postorder traversal of a graph
+/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
+/// order.
+///
+/// This is clearer than writing `preorder` in cases where the order doesn't matter.
+pub fn reachable<'a, 'tcx>(
+ body: &'a Body<'tcx>,
+) -> impl 'a + Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> {
+ preorder(body)
+}
+
+/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
+pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
+ let mut iter = preorder(body);
+ iter.by_ref().for_each(drop);
+ iter.visited
+}
+
+/// Reverse postorder traversal of a graph.
+///
+/// This function creates an iterator over the `Body`'s basic blocks, that:
+/// - returns basic blocks in a reverse postorder,
+/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
///
/// Reverse postorder is the reverse order of a postorder traversal.
/// This is different to a preorder traversal and represents a natural
@@ -246,65 +275,6 @@ pub fn postorder<'a, 'tcx>(
/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
/// Note that for a graph containing no loops (i.e., A DAG), this is equivalent to
/// a topological sort.
-///
-/// Construction of a `ReversePostorder` traversal requires doing a full
-/// postorder traversal of the graph, therefore this traversal should be
-/// constructed as few times as possible. Use the `reset` method to be able
-/// to re-use the traversal
-#[derive(Clone)]
-pub struct ReversePostorder<'a, 'tcx> {
- body: &'a Body<'tcx>,
- blocks: Vec<BasicBlock>,
- idx: usize,
-}
-
-impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
- pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
- let blocks: Vec<_> = Postorder::new(&body.basic_blocks, root).map(|(bb, _)| bb).collect();
- let len = blocks.len();
- ReversePostorder { body, blocks, idx: len }
- }
-}
-
-impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
- type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
-
- fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
- if self.idx == 0 {
- return None;
- }
- self.idx -= 1;
-
- self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- (self.idx, Some(self.idx))
- }
-}
-
-impl<'a, 'tcx> ExactSizeIterator for ReversePostorder<'a, 'tcx> {}
-
-/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
-/// order.
-///
-/// This is clearer than writing `preorder` in cases where the order doesn't matter.
-pub fn reachable<'a, 'tcx>(
- body: &'a Body<'tcx>,
-) -> impl 'a + Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> {
- preorder(body)
-}
-
-/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
-pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
- let mut iter = preorder(body);
- (&mut iter).for_each(drop);
- iter.visited
-}
-
-/// Creates an iterator over the `Body`'s basic blocks, that:
-/// - returns basic blocks in a reverse postorder,
-/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
pub fn reverse_postorder<'a, 'tcx>(
body: &'a Body<'tcx>,
) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs
index 06874741b..8d427fdb6 100644
--- a/compiler/rustc_middle/src/mir/type_foldable.rs
+++ b/compiler/rustc_middle/src/mir/type_foldable.rs
@@ -5,7 +5,7 @@ use rustc_ast::InlineAsmTemplatePiece;
use super::*;
use crate::ty;
-TrivialTypeTraversalAndLiftImpls! {
+TrivialTypeTraversalImpls! {
BlockTailInfo,
MirPhase,
SourceInfo,
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 069b38591..f2745b32c 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -186,7 +186,7 @@ macro_rules! make_mir_visitor {
fn visit_constant(
&mut self,
- constant: & $($mutability)? Constant<'tcx>,
+ constant: & $($mutability)? ConstOperand<'tcx>,
location: Location,
) {
self.super_constant(constant, location);
@@ -469,8 +469,8 @@ macro_rules! make_mir_visitor {
self.visit_source_info(source_info);
match kind {
TerminatorKind::Goto { .. } |
- TerminatorKind::Resume |
- TerminatorKind::Terminate |
+ TerminatorKind::UnwindResume |
+ TerminatorKind::UnwindTerminate(_) |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
TerminatorKind::FalseEdge { .. } |
@@ -647,8 +647,8 @@ macro_rules! make_mir_visitor {
BorrowKind::Shared => PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow
),
- BorrowKind::Shallow => PlaceContext::NonMutatingUse(
- NonMutatingUseContext::ShallowBorrow
+ BorrowKind::Fake => PlaceContext::NonMutatingUse(
+ NonMutatingUseContext::FakeBorrow
),
BorrowKind::Mut { .. } =>
PlaceContext::MutatingUse(MutatingUseContext::Borrow),
@@ -838,12 +838,20 @@ macro_rules! make_mir_visitor {
let VarDebugInfo {
name: _,
source_info,
+ composite,
value,
argument_index: _,
} = var_debug_info;
self.visit_source_info(source_info);
let location = Location::START;
+ if let Some(box VarDebugInfoFragment { ref $($mutability)? ty, ref $($mutability)? projection }) = composite {
+ self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+ for elem in projection {
+ let ProjectionElem::Field(_, ty) = elem else { bug!() };
+ self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+ }
+ }
match value {
VarDebugInfoContents::Const(c) => self.visit_constant(c, location),
VarDebugInfoContents::Place(place) =>
@@ -852,17 +860,6 @@ macro_rules! make_mir_visitor {
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location
),
- VarDebugInfoContents::Composite { ty, fragments } => {
- // FIXME(eddyb) use a better `TyContext` here.
- self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
- for VarDebugInfoFragment { projection: _, contents } in fragments {
- self.visit_place(
- contents,
- PlaceContext::NonUse(NonUseContext::VarDebugInfo),
- location,
- );
- }
- }
}
}
@@ -873,20 +870,20 @@ macro_rules! make_mir_visitor {
fn super_constant(
&mut self,
- constant: & $($mutability)? Constant<'tcx>,
+ constant: & $($mutability)? ConstOperand<'tcx>,
location: Location
) {
- let Constant {
+ let ConstOperand {
span,
user_ty: _, // no visit method for this
- literal,
+ const_,
} = constant;
self.visit_span($(& $mutability)? *span);
- match literal {
- ConstantKind::Ty(ct) => self.visit_ty_const($(&$mutability)? *ct, location),
- ConstantKind::Val(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)),
- ConstantKind::Unevaluated(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)),
+ match const_ {
+ Const::Ty(ct) => self.visit_ty_const($(&$mutability)? *ct, location),
+ Const::Val(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)),
+ Const::Unevaluated(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)),
}
}
@@ -1112,6 +1109,11 @@ macro_rules! visit_place_fns {
self.visit_ty(&mut new_ty, TyContext::Location(location));
if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None }
}
+ PlaceElem::Subtype(ty) => {
+ let mut new_ty = ty;
+ self.visit_ty(&mut new_ty, TyContext::Location(location));
+ if ty != new_ty { Some(PlaceElem::Subtype(new_ty)) } else { None }
+ }
PlaceElem::Deref
| PlaceElem::ConstantIndex { .. }
| PlaceElem::Subslice { .. }
@@ -1178,7 +1180,9 @@ macro_rules! visit_place_fns {
location: Location,
) {
match elem {
- ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => {
+ ProjectionElem::OpaqueCast(ty)
+ | ProjectionElem::Subtype(ty)
+ | ProjectionElem::Field(_, ty) => {
self.visit_ty(ty, TyContext::Location(location));
}
ProjectionElem::Index(local) => {
@@ -1256,8 +1260,8 @@ pub enum NonMutatingUseContext {
Move,
/// Shared borrow.
SharedBorrow,
- /// Shallow borrow.
- ShallowBorrow,
+ /// A fake borrow.
+ FakeBorrow,
/// AddressOf for *const pointer.
AddressOf,
/// PlaceMention statement.
@@ -1336,7 +1340,7 @@ impl PlaceContext {
matches!(
self,
PlaceContext::NonMutatingUse(
- NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::ShallowBorrow
+ NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::FakeBorrow
) | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
)
}