summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/mir/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/mir/mod.rs')
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs1612
1 files changed, 98 insertions, 1514 deletions
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
}