use crate::mir::coverage::{CodeRegion, CoverageKind};
use crate::mir::interpret::{Allocation, GlobalAlloc, Scalar};
use crate::mir::visit::MirVisitable;
use crate::ty::adjustment::PointerCast;
use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::subst::{Subst, SubstsRef};
use crate::ty::{self, List, Ty, TyCtxt};
use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, GeneratorKind};
use rustc_target::abi::{Size, VariantIdx};
use polonius_engine::Atom;
pub use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::{dominators, Dominators};
use rustc_data_structures::graph::{self, GraphSuccessors};
use rustc_index::bit_set::BitMatrix;
use rustc_index::vec::{Idx, IndexVec};
use rustc_serialize::{Decodable, Encodable};
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass;
use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::ops::{ControlFlow, Index, IndexMut};
use std::slice;
use std::{iter, mem, option};
use self::graph_cyclic_cache::GraphIsCyclicCache;
use self::predecessors::{PredecessorCache, Predecessors};
pub use self::query::*;
pub mod abstract_const;
pub mod coverage;
mod graph_cyclic_cache;
pub mod interpret;
pub mod mono;
mod predecessors;
mod query;
pub mod tcx;
pub mod terminator;
pub use terminator::*;
pub mod traversal;
mod type_foldable;
pub mod visit;
pub type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
pub trait HasLocalDecls<'tcx> {
fn local_decls(&self) -> &LocalDecls<'tcx>;
}
impl<'tcx> HasLocalDecls<'tcx> for LocalDecls<'tcx> {
#[inline]
fn local_decls(&self) -> &LocalDecls<'tcx> {
self
}
}
impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> {
#[inline]
fn local_decls(&self) -> &LocalDecls<'tcx> {
&self.local_decls
}
}
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(HashStable)]
pub enum MirPhase {
Build = 0,
Const = 1,
ConstPromotion = 2,
DropLowering = 3,
GeneratorLowering = 4,
Optimization = 5,
}
impl MirPhase {
pub fn phase_index(&self) -> usize {
*self as usize
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable)]
pub struct MirSource<'tcx> {
pub instance: InstanceDef<'tcx>,
pub promoted: Option<Promoted>,
}
impl<'tcx> MirSource<'tcx> {
pub fn item(def_id: DefId) -> Self {
MirSource {
instance: InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)),
promoted: None,
}
}
pub fn from_instance(instance: InstanceDef<'tcx>) -> Self {
MirSource { instance, promoted: None }
}
pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> {
self.instance.with_opt_param()
}
#[inline]
pub fn def_id(&self) -> DefId {
self.instance.def_id()
}
}
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable)]
pub struct GeneratorInfo<'tcx> {
pub yield_ty: Option<Ty<'tcx>>,
pub generator_drop: Option<Body<'tcx>>,
pub generator_layout: Option<GeneratorLayout<'tcx>>,
pub generator_kind: GeneratorKind,
}
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable)]
pub struct Body<'tcx> {
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
pub phase: MirPhase,
pub source: MirSource<'tcx>,
pub source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
pub generator: Option<Box<GeneratorInfo<'tcx>>>,
pub local_decls: LocalDecls<'tcx>,
pub user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
pub arg_count: usize,
pub spread_arg: Option<Local>,
pub var_debug_info: Vec<VarDebugInfo<'tcx>>,
pub span: Span,
pub required_consts: Vec<Constant<'tcx>>,
pub is_polymorphic: bool,
predecessor_cache: PredecessorCache,
is_cyclic: GraphIsCyclicCache,
}
impl<'tcx> Body<'tcx> {
pub fn new(
source: MirSource<'tcx>,
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
local_decls: LocalDecls<'tcx>,
user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
arg_count: usize,
var_debug_info: Vec<VarDebugInfo<'tcx>>,
span: Span,
generator_kind: Option<GeneratorKind>,
) -> Self {
assert!(
local_decls.len() > arg_count,
"expected at least {} locals, got {}",
arg_count + 1,
local_decls.len()
);
let mut body = Body {
phase: MirPhase::Build,
source,
basic_blocks,
source_scopes,
generator: generator_kind.map(|generator_kind| {
Box::new(GeneratorInfo {
yield_ty: None,
generator_drop: None,
generator_layout: None,
generator_kind,
})
}),
local_decls,
user_type_annotations,
arg_count,
spread_arg: None,
var_debug_info,
span,
required_consts: Vec::new(),
is_polymorphic: false,
predecessor_cache: PredecessorCache::new(),
is_cyclic: GraphIsCyclicCache::new(),
};
body.is_polymorphic = body.has_param_types_or_consts();
body
}
pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
let mut body = Body {
phase: MirPhase::Build,
source: MirSource::item(DefId::local(CRATE_DEF_INDEX)),
basic_blocks,
source_scopes: IndexVec::new(),
generator: None,
local_decls: IndexVec::new(),
user_type_annotations: IndexVec::new(),
arg_count: 0,
spread_arg: None,
span: DUMMY_SP,
required_consts: Vec::new(),
var_debug_info: Vec::new(),
is_polymorphic: false,
predecessor_cache: PredecessorCache::new(),
is_cyclic: GraphIsCyclicCache::new(),
};
body.is_polymorphic = body.has_param_types_or_consts();
body
}
#[inline]
pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
&self.basic_blocks
}
#[inline]
pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
self.predecessor_cache.invalidate();
self.is_cyclic.invalidate();
&mut self.basic_blocks
}
#[inline]
pub fn basic_blocks_and_local_decls_mut(
&mut self,
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
self.predecessor_cache.invalidate();
self.is_cyclic.invalidate();
(&mut self.basic_blocks, &mut self.local_decls)
}
#[inline]
pub fn basic_blocks_local_decls_mut_and_var_debug_info(
&mut self,
) -> (
&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
&mut LocalDecls<'tcx>,
&mut Vec<VarDebugInfo<'tcx>>,
) {
self.predecessor_cache.invalidate();
self.is_cyclic.invalidate();
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
}
pub fn is_cfg_cyclic(&self) -> bool {
self.is_cyclic.is_cyclic(self)
}
#[inline]
pub fn local_kind(&self, local: Local) -> LocalKind {
let index = local.as_usize();
if index == 0 {
debug_assert!(
self.local_decls[local].mutability == Mutability::Mut,
"return place should be mutable"
);
LocalKind::ReturnPointer
} else if index < self.arg_count + 1 {
LocalKind::Arg
} else if self.local_decls[local].is_user_variable() {
LocalKind::Var
} else {
LocalKind::Temp
}
}
#[inline]
pub fn temps_iter<'a>(&'a self) -> impl Iterator<Item = Local> + 'a {
(self.arg_count + 1..self.local_decls.len()).filter_map(move |index| {
let local = Local::new(index);
if self.local_decls[local].is_user_variable() { None } else { Some(local) }
})
}
#[inline]
pub fn vars_iter<'a>(&'a self) -> impl Iterator<Item = Local> + 'a {
(self.arg_count + 1..self.local_decls.len()).filter_map(move |index| {
let local = Local::new(index);
self.local_decls[local].is_user_variable().then_some(local)
})
}
#[inline]
pub fn mut_vars_iter<'a>(&'a self) -> impl Iterator<Item = Local> + 'a {
(self.arg_count + 1..self.local_decls.len()).filter_map(move |index| {
let local = Local::new(index);
let decl = &self.local_decls[local];
if decl.is_user_variable() && decl.mutability == Mutability::Mut {
Some(local)
} else {
None
}
})
}
#[inline]
pub fn mut_vars_and_args_iter<'a>(&'a self) -> impl Iterator<Item = Local> + 'a {
(1..self.local_decls.len()).filter_map(move |index| {
let local = Local::new(index);
let decl = &self.local_decls[local];
if (decl.is_user_variable() || index < self.arg_count + 1)
&& decl.mutability == Mutability::Mut
{
Some(local)
} else {
None
}
})
}
#[inline]
pub fn args_iter(&self) -> impl Iterator<Item = Local> + ExactSizeIterator {
let arg_count = self.arg_count;
(1..arg_count + 1).map(Local::new)
}
#[inline]
pub fn vars_and_temps_iter(
&self,
) -> impl DoubleEndedIterator<Item = Local> + ExactSizeIterator {
let arg_count = self.arg_count;
let local_count = self.local_decls.len();
(arg_count + 1..local_count).map(Local::new)
}
pub fn make_statement_nop(&mut self, location: Location) {
let block = &mut self.basic_blocks[location.block];
debug_assert!(location.statement_index < block.statements.len());
block.statements[location.statement_index].make_nop()
}
pub fn source_info(&self, location: Location) -> &SourceInfo {
let block = &self[location.block];
let stmts = &block.statements;
let idx = location.statement_index;
if idx < stmts.len() {
&stmts[idx].source_info
} else {
assert_eq!(idx, stmts.len());
&block.terminator().source_info
}
}
#[inline]
pub fn return_ty(&self) -> Ty<'tcx> {
self.local_decls[RETURN_PLACE].ty
}
#[inline]
pub fn terminator_loc(&self, bb: BasicBlock) -> Location {
Location { block: bb, statement_index: self[bb].statements.len() }
}
#[inline]
pub fn predecessors(&self) -> impl std::ops::Deref<Target = Predecessors> + '_ {
self.predecessor_cache.compute(&self.basic_blocks)
}
#[inline]
pub fn dominators(&self) -> Dominators<BasicBlock> {
dominators(self)
}
#[inline]
pub fn yield_ty(&self) -> Option<Ty<'tcx>> {
self.generator.as_ref().and_then(|generator| generator.yield_ty)
}
#[inline]
pub fn generator_layout(&self) -> Option<&GeneratorLayout<'tcx>> {
self.generator.as_ref().and_then(|generator| generator.generator_layout.as_ref())
}
#[inline]
pub fn generator_drop(&self) -> Option<&Body<'tcx>> {
self.generator.as_ref().and_then(|generator| generator.generator_drop.as_ref())
}
#[inline]
pub fn generator_kind(&self) -> Option<GeneratorKind> {
self.generator.as_ref().map(|generator| generator.generator_kind)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)]
pub enum Safety {
Safe,
BuiltinUnsafe,
FnUnsafe,
ExplicitUnsafe(hir::HirId),
}
impl<'tcx> Index<BasicBlock> for Body<'tcx> {
type Output = BasicBlockData<'tcx>;
#[inline]
fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
&self.basic_blocks()[index]
}
}
impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> {
#[inline]
fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> {
&mut self.basic_blocks_mut()[index]
}
}
#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)]
pub enum ClearCrossCrate<T> {
Clear,
Set(T),
}
impl<T> ClearCrossCrate<T> {
pub fn as_ref(&self) -> ClearCrossCrate<&T> {
match self {
ClearCrossCrate::Clear => ClearCrossCrate::Clear,
ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v),
}
}
pub fn assert_crate_local(self) -> T {
match self {
ClearCrossCrate::Clear => bug!("unwrapping cross-crate data"),
ClearCrossCrate::Set(v) => v,
}
}
}
const TAG_CLEAR_CROSS_CRATE_CLEAR: u8 = 0;
const TAG_CLEAR_CROSS_CRATE_SET: u8 = 1;
impl<'tcx, E: TyEncoder<'tcx>, T: Encodable<E>> Encodable<E> for ClearCrossCrate<T> {
#[inline]
fn encode(&self, e: &mut E) -> Result<(), E::Error> {
if E::CLEAR_CROSS_CRATE {
return Ok(());
}
match *self {
ClearCrossCrate::Clear => TAG_CLEAR_CROSS_CRATE_CLEAR.encode(e),
ClearCrossCrate::Set(ref val) => {
TAG_CLEAR_CROSS_CRATE_SET.encode(e)?;
val.encode(e)
}
}
}
}
impl<'tcx, D: TyDecoder<'tcx>, T: Decodable<D>> Decodable<D> for ClearCrossCrate<T> {
#[inline]
fn decode(d: &mut D) -> Result<ClearCrossCrate<T>, D::Error> {
if D::CLEAR_CROSS_CRATE {
return Ok(ClearCrossCrate::Clear);
}
let discr = u8::decode(d)?;
match discr {
TAG_CLEAR_CROSS_CRATE_CLEAR => Ok(ClearCrossCrate::Clear),
TAG_CLEAR_CROSS_CRATE_SET => {
let val = T::decode(d)?;
Ok(ClearCrossCrate::Set(val))
}
tag => Err(d.error(&format!("Invalid tag for ClearCrossCrate: {:?}", tag))),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
pub struct SourceInfo {
pub span: Span,
pub scope: SourceScope,
}
impl SourceInfo {
#[inline]
pub fn outermost(span: Span) -> Self {
SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)]
#[derive(Hash, HashStable)]
pub enum BorrowKind {
Shared,
Shallow,
Unique,
Mut {
allow_two_phase_borrow: bool,
},
}
impl BorrowKind {
pub fn allows_two_phase_borrow(&self) -> bool {
match *self {
BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false,
BorrowKind::Mut { allow_two_phase_borrow } => allow_two_phase_borrow,
}
}
}
rustc_index::newtype_index! {
pub struct Local {
derive [HashStable]
DEBUG_FORMAT = "_{}",
const RETURN_PLACE = 0,
}
}
impl Atom for Local {
fn index(self) -> usize {
Idx::index(self)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)]
pub enum LocalKind {
Var,
Temp,
Arg,
ReturnPointer,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct VarBindingForm<'tcx> {
pub binding_mode: ty::BindingMode,
pub opt_ty_info: Option<Span>,
pub opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
pub pat_span: Span,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable)]
pub enum BindingForm<'tcx> {
Var(VarBindingForm<'tcx>),
ImplicitSelf(ImplicitSelfKind),
RefForGuard,
}
#[derive(Clone, Copy, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)]
pub enum ImplicitSelfKind {
Imm,
Mut,
ImmRef,
MutRef,
None,
}
TrivialTypeFoldableAndLiftImpls! { BindingForm<'tcx>, }
mod binding_form_impl {
use crate::ich::StableHashingContext;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for super::BindingForm<'tcx> {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
use super::BindingForm::*;
std::mem::discriminant(self).hash_stable(hcx, hasher);
match self {
Var(binding) => binding.hash_stable(hcx, hasher),
ImplicitSelf(kind) => kind.hash_stable(hcx, hasher),
RefForGuard => (),
}
}
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct BlockTailInfo {
pub tail_result_is_ignored: bool,
pub span: Span,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub struct LocalDecl<'tcx> {
pub mutability: Mutability,
pub local_info: Option<Box<LocalInfo<'tcx>>>,
pub internal: bool,
pub is_block_tail: Option<BlockTailInfo>,
pub ty: Ty<'tcx>,
pub user_ty: Option<Box<UserTypeProjections>>,
pub source_info: SourceInfo,
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(LocalDecl<'_>, 56);
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub enum LocalInfo<'tcx> {
User(ClearCrossCrate<BindingForm<'tcx>>),
StaticRef { def_id: DefId, is_thread_local: bool },
ConstRef { def_id: DefId },
}
impl<'tcx> LocalDecl<'tcx> {
pub fn can_be_made_mutable(&self) -> bool {
matches!(
self.local_info,
Some(box LocalInfo::User(ClearCrossCrate::Set(
BindingForm::Var(VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_),
opt_ty_info: _,
opt_match_place: _,
pat_span: _,
}) | BindingForm::ImplicitSelf(ImplicitSelfKind::Imm),
)))
)
}
pub fn is_nonref_binding(&self) -> bool {
matches!(
self.local_info,
Some(box LocalInfo::User(ClearCrossCrate::Set(
BindingForm::Var(VarBindingForm {
binding_mode: ty::BindingMode::BindByValue(_),
opt_ty_info: _,
opt_match_place: _,
pat_span: _,
}) | BindingForm::ImplicitSelf(_),
)))
)
}
#[inline]
pub fn is_user_variable(&self) -> bool {
matches!(self.local_info, Some(box LocalInfo::User(_)))
}
pub fn is_ref_for_guard(&self) -> bool {
matches!(
self.local_info,
Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard)))
)
}
pub fn is_ref_to_static(&self) -> bool {
matches!(self.local_info, Some(box LocalInfo::StaticRef { .. }))
}
pub fn is_ref_to_thread_local(&self) -> bool {
match self.local_info {
Some(box LocalInfo::StaticRef { is_thread_local, .. }) => is_thread_local,
_ => false,
}
}
#[inline]
pub fn from_compiler_desugaring(&self) -> bool {
self.source_info.span.desugaring_kind().is_some()
}
#[inline]
pub fn new(ty: Ty<'tcx>, span: Span) -> Self {
Self::with_source_info(ty, SourceInfo::outermost(span))
}
#[inline]
pub fn with_source_info(ty: Ty<'tcx>, source_info: SourceInfo) -> Self {
LocalDecl {
mutability: Mutability::Mut,
local_info: None,
internal: false,
is_block_tail: None,
ty,
user_ty: None,
source_info,
}
}
#[inline]
pub fn internal(mut self) -> Self {
self.internal = true;
self
}
#[inline]
pub fn immutable(mut self) -> Self {
self.mutability = Mutability::Not;
self
}
#[inline]
pub fn block_tail(mut self, info: BlockTailInfo) -> Self {
assert!(self.is_block_tail.is_none());
self.is_block_tail = Some(info);
self
}
}
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub enum VarDebugInfoContents<'tcx> {
Place(Place<'tcx>),
Const(Constant<'tcx>),
}
impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match self {
VarDebugInfoContents::Const(c) => write!(fmt, "{}", c),
VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p),
}
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub struct VarDebugInfo<'tcx> {
pub name: Symbol,
pub source_info: SourceInfo,
pub value: VarDebugInfoContents<'tcx>,
}
rustc_index::newtype_index! {
pub struct BasicBlock {
derive [HashStable]
DEBUG_FORMAT = "bb{}",
const START_BLOCK = 0,
}
}
impl BasicBlock {
pub fn start_location(self) -> Location {
Location { block: self, statement_index: 0 }
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub struct BasicBlockData<'tcx> {
pub statements: Vec<Statement<'tcx>>,
pub terminator: Option<Terminator<'tcx>>,
pub is_cleanup: bool,
}
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, PartialOrd)]
pub enum AssertKind<O> {
BoundsCheck { len: O, index: O },
Overflow(BinOp, O, O),
OverflowNeg(O),
DivisionByZero(O),
RemainderByZero(O),
ResumedAfterReturn(GeneratorKind),
ResumedAfterPanic(GeneratorKind),
}
#[derive(
Clone,
Debug,
PartialEq,
PartialOrd,
TyEncodable,
TyDecodable,
Hash,
HashStable,
TypeFoldable
)]
pub enum InlineAsmOperand<'tcx> {
In {
reg: InlineAsmRegOrRegClass,
value: Operand<'tcx>,
},
Out {
reg: InlineAsmRegOrRegClass,
late: bool,
place: Option<Place<'tcx>>,
},
InOut {
reg: InlineAsmRegOrRegClass,
late: bool,
in_value: Operand<'tcx>,
out_place: Option<Place<'tcx>>,
},
Const {
value: Operand<'tcx>,
},
SymFn {
value: Box<Constant<'tcx>>,
},
SymStatic {
def_id: DefId,
},
}
pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>;
pub type Successors<'a> =
iter::Chain<option::IntoIter<&'a BasicBlock>, slice::Iter<'a, BasicBlock>>;
pub type SuccessorsMut<'a> =
iter::Chain<option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>;
impl<'tcx> BasicBlockData<'tcx> {
pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> {
BasicBlockData { statements: vec![], terminator, is_cleanup: false }
}
pub fn terminator(&self) -> &Terminator<'tcx> {
self.terminator.as_ref().expect("invalid terminator state")
}
pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> {
self.terminator.as_mut().expect("invalid terminator state")
}
pub fn retain_statements<F>(&mut self, mut f: F)
where
F: FnMut(&mut Statement<'_>) -> bool,
{
for s in &mut self.statements {
if !f(s) {
s.make_nop();
}
}
}
pub fn expand_statements<F, I>(&mut self, mut f: F)
where
F: FnMut(&mut Statement<'tcx>) -> Option<I>,
I: iter::TrustedLen<Item = Statement<'tcx>>,
{
let mut splices: Vec<(usize, I)> = vec![];
let mut extra_stmts = 0;
for (i, s) in self.statements.iter_mut().enumerate() {
if let Some(mut new_stmts) = f(s) {
if let Some(first) = new_stmts.next() {
*s = first;
let remaining = new_stmts.size_hint().0;
if remaining > 0 {
splices.push((i + 1 + extra_stmts, new_stmts));
extra_stmts += remaining;
}
} else {
s.make_nop();
}
}
}
let mut gap = self.statements.len()..self.statements.len() + extra_stmts;
self.statements.resize(
gap.end,
Statement { source_info: SourceInfo::outermost(DUMMY_SP), kind: StatementKind::Nop },
);
for (splice_start, new_stmts) in splices.into_iter().rev() {
let splice_end = splice_start + new_stmts.size_hint().0;
while gap.end > splice_end {
gap.start -= 1;
gap.end -= 1;
self.statements.swap(gap.start, gap.end);
}
self.statements.splice(splice_start..splice_end, new_stmts);
gap.end = splice_start;
}
}
pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> {
if index < self.statements.len() { &self.statements[index] } else { &self.terminator }
}
}
impl<O> AssertKind<O> {
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 { .. } => bug!("Unexpected AssertKind"),
}
}
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)
}
_ => write!(f, "\"{}\"", self.description()),
}
}
}
impl<O: fmt::Debug> fmt::Debug for AssertKind<O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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)
}
_ => write!(f, "{}", self.description()),
}
}
}
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub struct Statement<'tcx> {
pub source_info: SourceInfo,
pub kind: StatementKind<'tcx>,
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(Statement<'_>, 32);
impl Statement<'_> {
pub fn make_nop(&mut self) {
self.kind = StatementKind::Nop
}
pub fn replace_nop(&mut self) -> Self {
Statement {
source_info: self.source_info,
kind: mem::replace(&mut self.kind, StatementKind::Nop),
}
}
}
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
pub enum StatementKind<'tcx> {
Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>),
FakeRead(FakeReadCause, Box<Place<'tcx>>),
SetDiscriminant { place: Box<Place<'tcx>>, variant_index: VariantIdx },
StorageLive(Local),
StorageDead(Local),
LlvmInlineAsm(Box<LlvmInlineAsm<'tcx>>),
Retag(RetagKind, Box<Place<'tcx>>),
AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance),
Coverage(Box<Coverage>),
CopyNonOverlapping(Box<CopyNonOverlapping<'tcx>>),
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,
}
}
}
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, Hash, HashStable)]
pub enum RetagKind {
FnEntry,
TwoPhase,
Raw,
Default,
}
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, Hash, HashStable, PartialEq)]
pub enum FakeReadCause {
ForMatchGuard,
ForMatchedPlace,
ForGuardBinding,
ForLet,
ForIndex,
}
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
pub struct LlvmInlineAsm<'tcx> {
pub asm: hir::LlvmInlineAsmInner,
pub outputs: Box<[Place<'tcx>]>,
pub inputs: Box<[(Span, Operand<'tcx>)]>,
}
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(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)
}
LlvmInlineAsm(ref asm) => {
write!(fmt, "llvm_asm!({:?} : {:?} : {:?})", asm.asm, asm.outputs, asm.inputs)
}
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty)
}
Coverage(box ref coverage) => {
if let Some(rgn) = &coverage.code_region {
write!(fmt, "Coverage::{:?} for {:?}", coverage.kind, rgn)
} else {
write!(fmt, "Coverage::{:?}", coverage.kind)
}
}
CopyNonOverlapping(box crate::mir::CopyNonOverlapping {
ref src,
ref dst,
ref count,
}) => {
write!(fmt, "copy_nonoverlapping(src={:?}, dst={:?}, count={:?})", src, dst, count)
}
Nop => write!(fmt, "nop"),
}
}
}
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
pub struct Coverage {
pub kind: CoverageKind,
pub code_region: Option<CodeRegion>,
}
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
pub struct CopyNonOverlapping<'tcx> {
pub src: Operand<'tcx>,
pub dst: Operand<'tcx>,
pub count: Operand<'tcx>,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, HashStable)]
pub struct Place<'tcx> {
pub local: Local,
pub projection: &'tcx List<PlaceElem<'tcx>>,
}
#[cfg(target_arch = "x86_64")]
static_assert_size!(Place<'_>, 16);
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(TyEncodable, TyDecodable, HashStable)]
pub enum ProjectionElem<V, T> {
Deref,
Field(Field, T),
Index(V),
ConstantIndex {
offset: u64,
min_length: u64,
from_end: bool,
},
Subslice {
from: u64,
to: u64,
from_end: bool,
},
Downcast(Option<Symbol>, VariantIdx),
}
impl<V, T> ProjectionElem<V, T> {
fn is_indirect(&self) -> bool {
match self {
Self::Deref => true,
Self::Field(_, _)
| Self::Index(_)
| Self::ConstantIndex { .. }
| Self::Subslice { .. }
| Self::Downcast(_, _) => false,
}
}
}
pub type PlaceElem<'tcx> = ProjectionElem<Local, Ty<'tcx>>;
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(PlaceElem<'_>, 24);
pub type ProjectionKind = ProjectionElem<(), ()>;
rustc_index::newtype_index! {
pub struct Field {
derive [HashStable]
DEBUG_FORMAT = "field[{}]"
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PlaceRef<'tcx> {
pub local: Local,
pub projection: &'tcx [PlaceElem<'tcx>],
}
impl<'tcx> Place<'tcx> {
pub fn return_place() -> Place<'tcx> {
Place { local: RETURN_PLACE, projection: List::empty() }
}
pub fn is_indirect(&self) -> bool {
self.projection.iter().any(|elem| elem.is_indirect())
}
#[inline(always)]
pub fn local_or_deref_local(&self) -> Option<Local> {
self.as_ref().local_or_deref_local()
}
#[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 }
}
#[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)
})
}
}
impl From<Local> for Place<'_> {
fn from(local: Local) -> Self {
Place { local, projection: List::empty() }
}
}
impl<'tcx> PlaceRef<'tcx> {
pub fn local_or_deref_local(&self) -> Option<Local> {
match *self {
PlaceRef { local, projection: [] }
| PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
_ => None,
}
}
pub fn as_local(&self) -> Option<Local> {
match *self {
PlaceRef { local, projection: [] } => Some(local),
_ => None,
}
}
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
}
}
}
impl Debug for Place<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for elem in self.projection.iter().rev() {
match elem {
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::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, "[{:?} of {:?}]", offset, min_length)?;
}
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
write!(fmt, "[-{:?} of {:?}]", offset, 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(())
}
}
rustc_index::newtype_index! {
pub struct SourceScope {
derive [HashStable]
DEBUG_FORMAT = "scope[{}]",
const OUTERMOST_SOURCE_SCOPE = 0,
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub struct SourceScopeData<'tcx> {
pub span: Span,
pub parent_scope: Option<SourceScope>,
pub inlined: Option<(ty::Instance<'tcx>, Span)>,
pub inlined_parent_scope: Option<SourceScope>,
pub local_data: ClearCrossCrate<SourceScopeLocalData>,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct SourceScopeLocalData {
pub lint_root: hir::HirId,
pub safety: Safety,
}
#[derive(Clone, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum Operand<'tcx> {
Copy(Place<'tcx>),
Move(Place<'tcx>),
Constant(Box<Constant<'tcx>>),
}
#[cfg(target_arch = "x86_64")]
static_assert_size!(Operand<'_>, 24);
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> {
pub fn function_handle(
tcx: TyCtxt<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
span: Span,
) -> Self {
let ty = tcx.type_of(def_id).subst(tcx, substs);
Operand::Constant(box Constant {
span,
user_ty: None,
literal: ConstantKind::Ty(ty::Const::zero_sized(tcx, ty)),
})
}
pub fn is_move(&self) -> bool {
matches!(self, Operand::Move(..))
}
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 Constant {
span,
user_ty: None,
literal: ConstantKind::Val(val.into(), ty),
})
}
pub fn to_copy(&self) -> Self {
match *self {
Operand::Copy(_) | Operand::Constant(_) => self.clone(),
Operand::Move(place) => Operand::Copy(place),
}
}
pub fn place(&self) -> Option<Place<'tcx>> {
match self {
Operand::Copy(place) | Operand::Move(place) => Some(*place),
Operand::Constant(_) => None,
}
}
pub fn constant(&self) -> Option<&Constant<'tcx>> {
match self {
Operand::Constant(x) => Some(&**x),
Operand::Copy(_) | Operand::Move(_) => None,
}
}
}
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
pub enum Rvalue<'tcx> {
Use(Operand<'tcx>),
Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>),
Ref(Region<'tcx>, BorrowKind, Place<'tcx>),
ThreadLocalRef(DefId),
AddressOf(Mutability, Place<'tcx>),
Len(Place<'tcx>),
Cast(CastKind, Operand<'tcx>, Ty<'tcx>),
BinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>),
CheckedBinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>),
NullaryOp(NullOp, Ty<'tcx>),
UnaryOp(UnOp, Operand<'tcx>),
Discriminant(Place<'tcx>),
Aggregate(Box<AggregateKind<'tcx>>, Vec<Operand<'tcx>>),
}
#[cfg(target_arch = "x86_64")]
static_assert_size!(Rvalue<'_>, 40);
#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum CastKind {
Misc,
Pointer(PointerCast),
}
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum AggregateKind<'tcx> {
Array(Ty<'tcx>),
Tuple,
Adt(&'tcx AdtDef, VariantIdx, SubstsRef<'tcx>, Option<UserTypeAnnotationIndex>, Option<usize>),
Closure(DefId, SubstsRef<'tcx>),
Generator(DefId, SubstsRef<'tcx>, hir::Movability),
}
#[cfg(target_arch = "x86_64")]
static_assert_size!(AggregateKind<'_>, 48);
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Rem,
BitXor,
BitAnd,
BitOr,
Shl,
Shr,
Eq,
Lt,
Le,
Ne,
Ge,
Gt,
Offset,
}
impl BinOp {
pub fn is_checkable(self) -> bool {
use self::BinOp::*;
matches!(self, Add | Sub | Mul | Shl | Shr)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum NullOp {
SizeOf,
Box,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum UnOp {
Not,
Neg,
}
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, ref 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, "{:?} as {:?} ({:?})", place, 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) => write!(fmt, "{:?}({:?})", op, t),
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 { .. } | BorrowKind::Unique => "mut ",
};
let print_region = ty::tls::with(|tcx| {
tcx.sess.verbose() || tcx.sess.opts.debugging_opts.identify_regions
});
let region = if print_region {
let mut region = region.to_string();
if !region.is_empty() {
region.push(' ');
}
region
} else {
String::new()
};
write!(fmt, "&{}{}{:?}", region, kind_str, 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_def, variant, substs, _user_ty, _) => {
let variant_def = &adt_def.variants[variant];
let name = ty::tls::with(|tcx| {
let mut name = String::new();
let substs = tcx.lift(substs).expect("could not lift for printing");
FmtPrinter::new(tcx, &mut name, Namespace::ValueNS)
.print_def_path(variant_def.def_id, substs)?;
Ok(name)
})?;
match variant_def.ctor_kind {
CtorKind::Const => fmt.write_str(&name),
CtorKind::Fn => fmt_tuple(fmt, &name),
CtorKind::Fictive => {
let mut struct_fmt = fmt.debug_struct(&name);
for (field, place) in variant_def.fields.iter().zip(places) {
struct_fmt.field(&field.ident.as_str(), place);
}
struct_fmt.finish()
}
}
}
AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| {
if let Some(def_id) = def_id.as_local() {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let name = if tcx.sess.opts.debugging_opts.span_free_formats {
let substs = tcx.lift(substs).unwrap();
format!(
"[closure@{}]",
tcx.def_path_str_with_substs(def_id.to_def_id(), substs),
)
} else {
let span = tcx.hir().span(hir_id);
format!("[closure@{}]", tcx.sess.source_map().span_to_string(span))
};
let mut struct_fmt = fmt.debug_struct(&name);
if let Some(upvars) = tcx.upvars_mentioned(def_id) {
for (&var_id, place) in upvars.keys().zip(places) {
let var_name = tcx.hir().name(var_id);
struct_fmt.field(&var_name.as_str(), place);
}
}
struct_fmt.finish()
} else {
write!(fmt, "[closure]")
}
}),
AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
if let Some(def_id) = def_id.as_local() {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let name = format!("[generator@{:?}]", tcx.hir().span(hir_id));
let mut struct_fmt = fmt.debug_struct(&name);
if let Some(upvars) = tcx.upvars_mentioned(def_id) {
for (&var_id, place) in upvars.keys().zip(places) {
let var_name = tcx.hir().name(var_id);
struct_fmt.field(&var_name.as_str(), place);
}
}
struct_fmt.finish()
} else {
write!(fmt, "[generator]")
}
}),
}
}
}
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable)]
pub struct Constant<'tcx> {
pub span: Span,
pub user_ty: Option<UserTypeAnnotationIndex>,
pub literal: ConstantKind<'tcx>,
}
#[derive(Clone, Copy, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
pub enum ConstantKind<'tcx> {
Ty(&'tcx ty::Const<'tcx>),
Val(interpret::ConstValue<'tcx>, Ty<'tcx>),
}
impl Constant<'tcx> {
pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
match self.literal.const_for_ty()?.val.try_to_scalar() {
Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) {
GlobalAlloc::Static(def_id) => {
assert!(!tcx.is_thread_local_static(def_id));
Some(def_id)
}
_ => None,
},
_ => None,
}
}
pub fn ty(&self) -> Ty<'tcx> {
self.literal.ty()
}
}
impl From<&'tcx ty::Const<'tcx>> for ConstantKind<'tcx> {
fn from(ct: &'tcx ty::Const<'tcx>) -> Self {
Self::Ty(ct)
}
}
impl ConstantKind<'tcx> {
pub fn const_for_ty(&self) -> Option<&'tcx ty::Const<'tcx>> {
match self {
ConstantKind::Ty(c) => Some(c),
ConstantKind::Val(..) => None,
}
}
pub fn ty(&self) -> Ty<'tcx> {
match self {
ConstantKind::Ty(c) => c.ty,
ConstantKind::Val(_, ty) => ty,
}
}
#[inline]
pub fn try_to_value(self) -> Option<interpret::ConstValue<'tcx>> {
match self {
ConstantKind::Ty(c) => c.val.try_to_value(),
ConstantKind::Val(val, _) => Some(val),
}
}
#[inline]
pub fn try_to_scalar(self) -> Option<Scalar> {
self.try_to_value()?.try_to_scalar()
}
#[inline]
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
Some(self.try_to_value()?.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 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)
}
}
}
#[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(),
}
}
#[inline]
pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<u64> {
match self {
Self::Ty(ct) => ct.try_eval_usize(tcx, param_env),
Self::Val(val, _) => val.try_to_machine_usize(tcx),
}
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub struct UserTypeProjections {
pub contents: Vec<(UserTypeProjection, Span)>,
}
impl<'tcx> UserTypeProjections {
pub fn none() -> Self {
UserTypeProjections { contents: vec![] }
}
pub fn is_empty(&self) -> bool {
self.contents.is_empty()
}
pub fn projections_and_spans(
&self,
) -> impl Iterator<Item = &(UserTypeProjection, Span)> + ExactSizeIterator {
self.contents.iter()
}
pub fn projections(&self) -> impl Iterator<Item = &UserTypeProjection> + ExactSizeIterator {
self.contents.iter().map(|&(ref user_type, _span)| user_type)
}
pub fn push_projection(mut self, user_ty: &UserTypeProjection, span: Span) -> Self {
self.contents.push((user_ty.clone(), span));
self
}
fn map_projections(
mut self,
mut f: impl FnMut(UserTypeProjection) -> UserTypeProjection,
) -> Self {
self.contents = self.contents.drain(..).map(|(proj, span)| (f(proj), span)).collect();
self
}
pub fn index(self) -> Self {
self.map_projections(|pat_ty_proj| pat_ty_proj.index())
}
pub fn subslice(self, from: u64, to: u64) -> Self {
self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to))
}
pub fn deref(self) -> Self {
self.map_projections(|pat_ty_proj| pat_ty_proj.deref())
}
pub fn leaf(self, field: Field) -> Self {
self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field))
}
pub fn variant(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx, field: Field) -> Self {
self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field))
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
pub struct UserTypeProjection {
pub base: UserTypeAnnotationIndex,
pub projs: Vec<ProjectionKind>,
}
impl Copy for ProjectionKind {}
impl UserTypeProjection {
pub(crate) fn index(mut self) -> Self {
self.projs.push(ProjectionElem::Index(()));
self
}
pub(crate) fn subslice(mut self, from: u64, to: u64) -> Self {
self.projs.push(ProjectionElem::Subslice { from, to, from_end: true });
self
}
pub(crate) fn deref(mut self) -> Self {
self.projs.push(ProjectionElem::Deref);
self
}
pub(crate) fn leaf(mut self, field: Field) -> Self {
self.projs.push(ProjectionElem::Field(field, ()));
self
}
pub(crate) fn variant(
mut self,
adt_def: &AdtDef,
variant_index: VariantIdx,
field: Field,
) -> Self {
self.projs.push(ProjectionElem::Downcast(
Some(adt_def.variants[variant_index].ident.name),
variant_index,
));
self.projs.push(ProjectionElem::Field(field, ()));
self
}
}
TrivialTypeFoldableAndLiftImpls! { ProjectionKind, }
impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection {
fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
UserTypeProjection {
base: self.base.fold_with(folder),
projs: self.projs.fold_with(folder),
}
}
fn super_visit_with<Vs: TypeVisitor<'tcx>>(
&self,
visitor: &mut Vs,
) -> ControlFlow<Vs::BreakTy> {
self.base.visit_with(visitor)
}
}
rustc_index::newtype_index! {
pub struct Promoted {
derive [HashStable]
DEBUG_FORMAT = "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 ")?,
}
match self.literal {
ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt, true),
}
}
}
fn pretty_print_const(
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, fmt, Namespace::ValueNS);
cx.print_alloc_ids = true;
cx.pretty_print_const(literal, print_types)?;
Ok(())
})
}
fn pretty_print_const_value(
val: interpret::ConstValue<'tcx>,
ty: Ty<'tcx>,
fmt: &mut Formatter<'_>,
print_types: bool,
) -> fmt::Result {
use crate::ty::print::PrettyPrinter;
ty::tls::with(|tcx| {
let val = tcx.lift(val).unwrap();
let ty = tcx.lift(ty).unwrap();
let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS);
cx.print_alloc_ids = true;
cx.pretty_print_const_value(val, ty, print_types)?;
Ok(())
})
}
impl<'tcx> graph::DirectedGraph for Body<'tcx> {
type Node = BasicBlock;
}
impl<'tcx> graph::WithNumNodes for Body<'tcx> {
#[inline]
fn num_nodes(&self) -> usize {
self.basic_blocks.len()
}
}
impl<'tcx> graph::WithStartNode for Body<'tcx> {
#[inline]
fn start_node(&self) -> Self::Node {
START_BLOCK
}
}
impl<'tcx> graph::WithSuccessors for Body<'tcx> {
#[inline]
fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
self.basic_blocks[node].terminator().successors().cloned()
}
}
impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> {
type Item = BasicBlock;
type Iter = iter::Cloned<Successors<'b>>;
}
impl graph::GraphPredecessors<'graph> for Body<'tcx> {
type Item = BasicBlock;
type Iter = smallvec::IntoIter<[BasicBlock; 4]>;
}
impl graph::WithPredecessors for Body<'tcx> {
#[inline]
fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
self.predecessors()[node].clone().into_iter()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, HashStable)]
pub struct Location {
pub block: BasicBlock,
pub statement_index: usize,
}
impl fmt::Debug for Location {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{:?}[{}]", self.block, self.statement_index)
}
}
impl Location {
pub const START: Location = Location { block: START_BLOCK, statement_index: 0 };
pub fn successor_within_block(&self) -> Location {
Location { block: self.block, statement_index: self.statement_index + 1 }
}
pub fn is_predecessor_of<'tcx>(&self, other: Location, body: &Body<'tcx>) -> bool {
if self.block == other.block && self.statement_index < other.statement_index {
return true;
}
let predecessors = body.predecessors();
let mut queue: Vec<BasicBlock> = predecessors[other.block].to_vec();
let mut visited = FxHashSet::default();
while let Some(block) = queue.pop() {
if visited.insert(block) {
queue.extend(predecessors[block].iter().cloned());
} else {
continue;
}
if self.block == block {
return true;
}
}
false
}
pub fn dominates(&self, other: Location, dominators: &Dominators<BasicBlock>) -> bool {
if self.block == other.block {
self.statement_index <= other.statement_index
} else {
dominators.is_dominated_by(other.block, self.block)
}
}
}