use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use alloc::borrow::Cow;
use core::fmt::{self, Debug, Formatter};
use core::iter;
use core::ops::{Index, IndexMut};
pub use basic_blocks::BasicBlocks;
use either::Either;
use crate::polonius_engine::Atom;
use crate::rustc_abi::{FieldIdx, VariantIdx};
pub use crate::rustc_ast::{Mutability, Pinnedness};
use crate::rustc_data_structures::fx::{FxHashMap, FxHashSet};
use crate::rustc_data_structures::graph::dominators::Dominators;
use crate::rustc_errors::ErrorGuaranteed;
use crate::rustc_hir::def::{CtorKind, Namespace};
use crate::rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use crate::rustc_hir::{
self as hir, BindingMode, ByRef, CoroutineDesugaring, CoroutineKind, HirId, ImplicitSelfKind,
};
use crate::rustc_index::bit_set::DenseBitSet;
use crate::rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use crate::rustc_serialize::{Decodable, Encodable};
use crate::rustc_span::{DUMMY_SP, Span, Spanned, Symbol};
use tracing::{debug, trace};
pub use self::query::*;
use crate::rustc_middle::mir::interpret::{AllocRange, Scalar};
use crate::rustc_middle::ty::codec::{TyDecoder, TyEncoder};
use crate::rustc_middle::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths};
use crate::rustc_middle::ty::{
self, GenericArg, GenericArgsRef, Instance, InstanceKind, List, ShimKind, Ty, TyCtxt,
TypeVisitableExt, TypingEnv, UserTypeAnnotationIndex,
};
mod basic_blocks;
mod consts;
pub mod coverage;
pub mod generic_graphviz;
pub mod interpret;
pub mod pretty;
mod query;
mod statement;
mod syntax;
mod terminator;
pub mod traversal;
pub mod visit;
pub use consts::*;
use pretty::pretty_print_const_value;
pub use statement::*;
pub use syntax::*;
pub use terminator::*;
pub use self::pretty::{MirDumper, PassWhere, display_allocation, write_mir_pretty};
pub type LocalDecls<'tcx> = IndexSlice<Local, LocalDecl<'tcx>>;
pub trait HasLocalDecls<'tcx> {
fn local_decls(&self) -> &LocalDecls<'tcx>;
}
impl<'tcx> HasLocalDecls<'tcx> for IndexVec<Local, LocalDecl<'tcx>> {
#[inline]
fn local_decls(&self) -> &LocalDecls<'tcx> {
self
}
}
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
}
}
impl MirPhase {
pub fn name(&self) -> &'static str {
match *self {
MirPhase::Built => "built",
MirPhase::Analysis(AnalysisPhase::Initial) => "analysis",
MirPhase::Analysis(AnalysisPhase::PostCleanup) => "analysis-post-cleanup",
MirPhase::Runtime(RuntimePhase::Initial) => "runtime",
MirPhase::Runtime(RuntimePhase::PostCleanup) => "runtime-post-cleanup",
MirPhase::Runtime(RuntimePhase::Optimized) => "runtime-optimized",
}
}
pub fn index(&self) -> (usize, usize) {
match *self {
MirPhase::Built => (1, 1),
MirPhase::Analysis(analysis_phase) => (2, 1 + analysis_phase as usize),
MirPhase::Runtime(runtime_phase) => (3, 1 + runtime_phase as usize),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(StableHash, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
pub struct MirSource<'tcx> {
pub instance: InstanceKind<'tcx>,
pub promoted: Option<Promoted>,
}
impl<'tcx> MirSource<'tcx> {
pub fn item(def_id: DefId) -> Self {
MirSource { instance: InstanceKind::Item(def_id), promoted: None }
}
pub fn from_shim(shim: ShimKind<'tcx>) -> Self {
MirSource { instance: InstanceKind::Shim(shim), promoted: None }
}
#[inline]
pub fn def_id(&self) -> DefId {
self.instance.def_id()
}
}
#[derive(Clone, TyEncodable, TyDecodable, Debug, StableHash, TypeFoldable, TypeVisitable)]
pub struct CoroutineInfo<'tcx> {
pub yield_ty: Option<Ty<'tcx>>,
pub resume_ty: Option<Ty<'tcx>>,
pub coroutine_drop: Option<Body<'tcx>>,
pub coroutine_drop_async: Option<Body<'tcx>>,
pub coroutine_drop_proxy_async: Option<Body<'tcx>>,
pub coroutine_layout: Option<CoroutineLayout<'tcx>>,
pub coroutine_kind: CoroutineKind,
}
impl<'tcx> CoroutineInfo<'tcx> {
pub fn initial(
coroutine_kind: CoroutineKind,
yield_ty: Ty<'tcx>,
resume_ty: Ty<'tcx>,
) -> CoroutineInfo<'tcx> {
CoroutineInfo {
coroutine_kind,
yield_ty: Some(yield_ty),
resume_ty: Some(resume_ty),
coroutine_drop: None,
coroutine_drop_async: None,
coroutine_drop_proxy_async: None,
coroutine_layout: None,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, StableHash, TyEncodable, TyDecodable)]
#[derive(TypeFoldable, TypeVisitable)]
pub enum MentionedItem<'tcx> {
Fn(Ty<'tcx>),
Drop(Ty<'tcx>),
UnsizeCast { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> },
Closure(Ty<'tcx>),
}
#[derive(Clone, TyEncodable, TyDecodable, Debug, StableHash, TypeFoldable, TypeVisitable)]
pub struct Body<'tcx> {
pub basic_blocks: BasicBlocks<'tcx>,
pub phase: MirPhase,
pub pass_count: usize,
pub source: MirSource<'tcx>,
pub source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
pub coroutine: Option<Box<CoroutineInfo<'tcx>>>,
pub local_decls: IndexVec<Local, LocalDecl<'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: Option<Vec<ConstOperand<'tcx>>>,
pub mentioned_items: Option<Vec<Spanned<MentionedItem<'tcx>>>>,
pub is_polymorphic: bool,
pub injection_phase: Option<MirPhase>,
pub tainted_by_errors: Option<ErrorGuaranteed>,
#[type_foldable(identity)]
#[type_visitable(ignore)]
pub coverage_early_info: Option<Box<coverage::CoverageEarlyInfo>>,
#[type_foldable(identity)]
#[type_visitable(ignore)]
pub coverage_mir_info: Option<Box<coverage::CoverageMirInfo>>,
}
impl<'tcx> Body<'tcx> {
pub fn new(
source: MirSource<'tcx>,
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
arg_count: usize,
var_debug_info: Vec<VarDebugInfo<'tcx>>,
span: Span,
coroutine: Option<Box<CoroutineInfo<'tcx>>>,
tainted_by_errors: Option<ErrorGuaranteed>,
) -> Self {
assert!(
local_decls.len() > arg_count,
"expected at least {} locals, got {}",
arg_count + 1,
local_decls.len()
);
let mut body = Body {
phase: MirPhase::Built,
pass_count: 0,
source,
basic_blocks: BasicBlocks::new(basic_blocks),
source_scopes,
coroutine,
local_decls,
user_type_annotations,
arg_count,
spread_arg: None,
var_debug_info,
span,
required_consts: None,
mentioned_items: None,
is_polymorphic: false,
injection_phase: None,
tainted_by_errors,
coverage_early_info: None,
coverage_mir_info: None,
};
body.is_polymorphic = body.has_non_region_param();
body
}
pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
let mut body = Body {
phase: MirPhase::Built,
pass_count: 0,
source: MirSource::item(CRATE_DEF_ID.to_def_id()),
basic_blocks: BasicBlocks::new(basic_blocks),
source_scopes: IndexVec::new(),
coroutine: None,
local_decls: IndexVec::new(),
user_type_annotations: IndexVec::new(),
arg_count: 0,
spread_arg: None,
span: DUMMY_SP,
required_consts: None,
mentioned_items: None,
var_debug_info: Vec::new(),
is_polymorphic: false,
injection_phase: None,
tainted_by_errors: None,
coverage_early_info: None,
coverage_mir_info: None,
};
body.is_polymorphic = body.has_non_region_param();
body
}
#[inline]
pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
self.basic_blocks.as_mut()
}
pub fn typing_env(&self, tcx: TyCtxt<'tcx>) -> TypingEnv<'tcx> {
if tcx.use_typing_mode_post_typeck_until_borrowck() {
match self.phase {
MirPhase::Built if let Some(def_id) = self.source.def_id().as_local() => {
TypingEnv::new(
tcx.param_env(self.source.def_id()),
ty::TypingMode::borrowck(tcx, def_id),
)
}
MirPhase::Analysis(_) if let Some(def_id) = self.source.def_id().as_local() => {
TypingEnv::new(
tcx.param_env(self.source.def_id()),
ty::TypingMode::post_borrowck_analysis(tcx, def_id),
)
}
MirPhase::Built | MirPhase::Analysis(_) => {
TypingEnv::post_analysis(tcx, self.source.def_id())
}
MirPhase::Runtime(_) => TypingEnv::post_analysis(tcx, self.source.def_id()),
}
} else {
match self.phase {
MirPhase::Built | MirPhase::Analysis(_) => TypingEnv::new(
tcx.param_env(self.source.def_id()),
ty::TypingMode::non_body_analysis(),
),
MirPhase::Runtime(_) => TypingEnv::post_analysis(tcx, self.source.def_id()),
}
}
}
#[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 {
LocalKind::Temp
}
}
#[inline]
pub fn mut_vars_iter(&self) -> impl Iterator<Item = Local> {
(self.arg_count + 1..self.local_decls.len()).filter_map(move |index| {
let local = Local::new(index);
let decl = &self.local_decls[local];
(decl.is_user_variable() && decl.mutability.is_mut()).then_some(local)
})
}
#[inline]
pub fn mut_vars_and_args_iter(&self) -> impl Iterator<Item = Local> {
(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 + use<> {
(1..self.arg_count + 1).map(Local::new)
}
#[inline]
pub fn vars_and_temps_iter(
&self,
) -> impl DoubleEndedIterator<Item = Local> + ExactSizeIterator {
(self.arg_count + 1..self.local_decls.len()).map(Local::new)
}
#[inline]
pub fn drain_vars_and_temps(&mut self) -> impl Iterator<Item = LocalDecl<'tcx>> {
self.local_decls.drain(self.arg_count + 1..)
}
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 bound_return_ty(&self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
ty::EarlyBinder::bind(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() }
}
pub fn stmt_at(&self, location: Location) -> Either<&Statement<'tcx>, &Terminator<'tcx>> {
let Location { block, statement_index } = location;
let block_data = &self.basic_blocks[block];
block_data
.statements
.get(statement_index)
.map(Either::Left)
.unwrap_or_else(|| Either::Right(block_data.terminator()))
}
#[inline]
pub fn yield_ty(&self) -> Option<Ty<'tcx>> {
self.coroutine.as_ref().and_then(|coroutine| coroutine.yield_ty)
}
#[inline]
pub fn resume_ty(&self) -> Option<Ty<'tcx>> {
self.coroutine.as_ref().and_then(|coroutine| coroutine.resume_ty)
}
#[inline]
pub fn coroutine_layout_raw(&self) -> Option<&CoroutineLayout<'tcx>> {
self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_layout.as_ref())
}
#[inline]
pub fn coroutine_drop(&self) -> Option<&Body<'tcx>> {
self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop.as_ref())
}
#[inline]
pub fn coroutine_drop_async(&self) -> Option<&Body<'tcx>> {
self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop_async.as_ref())
}
#[inline]
pub fn coroutine_requires_async_drop(&self) -> bool {
self.coroutine_drop_async().is_some()
}
#[inline]
pub fn future_drop_poll(&self) -> Option<&Body<'tcx>> {
self.coroutine.as_ref().and_then(|coroutine| {
coroutine
.coroutine_drop_async
.as_ref()
.or(coroutine.coroutine_drop_proxy_async.as_ref())
})
}
#[inline]
pub fn coroutine_kind(&self) -> Option<CoroutineKind> {
self.coroutine.as_ref().map(|coroutine| coroutine.coroutine_kind)
}
#[inline]
pub fn should_skip(&self) -> bool {
let Some(injection_phase) = self.injection_phase else {
return false;
};
injection_phase > self.phase
}
#[inline]
pub fn is_custom_mir(&self) -> bool {
self.injection_phase.is_some()
}
fn try_const_mono_switchint<'a>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
block: &'a BasicBlockData<'tcx>,
) -> Option<(u128, &'a SwitchTargets)> {
let eval_mono_const = |constant: &ConstOperand<'tcx>| {
let typing_env = ty::TypingEnv::fully_monomorphized();
let mono_literal = instance.instantiate_mir_and_normalize_erasing_regions(
tcx,
typing_env,
crate::rustc_middle::ty::EarlyBinder::bind(tcx, constant.const_),
);
mono_literal.try_eval_bits(tcx, typing_env)
};
let TerminatorKind::SwitchInt { discr, targets } = &block.terminator().kind else {
return None;
};
let discr = match discr {
Operand::Constant(constant) => {
let bits = eval_mono_const(constant)?;
return Some((bits, targets));
}
Operand::RuntimeChecks(check) => {
let bits = check.value(tcx.sess) as u128;
return Some((bits, targets));
}
Operand::Move(place) | Operand::Copy(place) => place,
};
let last_stmt = block.statements.iter().rev().find(|stmt| {
!matches!(stmt.kind, StatementKind::StorageDead(_) | StatementKind::StorageLive(_))
})?;
let (place, rvalue) = last_stmt.kind.as_assign()?;
if discr != place {
return None;
}
match rvalue {
Rvalue::Use(Operand::Constant(constant), _) => {
let bits = eval_mono_const(constant)?;
Some((bits, targets))
}
_ => None,
}
}
pub fn caller_location_span<T>(
&self,
mut source_info: SourceInfo,
caller_location: Option<T>,
tcx: TyCtxt<'tcx>,
from_span: impl FnOnce(Span) -> T,
) -> T {
loop {
let scope_data = &self.source_scopes[source_info.scope];
if let Some((callee, callsite_span)) = scope_data.inlined {
if !callee.def.requires_caller_location(tcx) {
return from_span(source_info.span);
}
source_info.span = callsite_span;
}
match scope_data.inlined_parent_scope {
Some(parent) => source_info.scope = parent,
None => break,
}
}
caller_location.unwrap_or_else(|| from_span(source_info.span))
}
#[track_caller]
pub fn set_required_consts(&mut self, required_consts: Vec<ConstOperand<'tcx>>) {
assert!(
self.required_consts.is_none(),
"required_consts for {:?} have already been set",
self.source.def_id()
);
self.required_consts = Some(required_consts);
}
#[track_caller]
pub fn required_consts(&self) -> &[ConstOperand<'tcx>] {
match &self.required_consts {
Some(l) => l,
None => panic!("required_consts for {:?} have not yet been set", self.source.def_id()),
}
}
#[track_caller]
pub fn set_mentioned_items(&mut self, mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>) {
assert!(
self.mentioned_items.is_none(),
"mentioned_items for {:?} have already been set",
self.source.def_id()
);
self.mentioned_items = Some(mentioned_items);
}
#[track_caller]
pub fn mentioned_items(&self) -> &[Spanned<MentionedItem<'tcx>>] {
match &self.mentioned_items {
Some(l) => l,
None => panic!("mentioned_items for {:?} have not yet been set", self.source.def_id()),
}
}
}
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.as_mut()[index]
}
}
#[derive(Copy, Clone, Debug, StableHash, TypeFoldable, TypeVisitable)]
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 as_mut(&mut self) -> ClearCrossCrate<&mut T> {
match self {
ClearCrossCrate::Clear => ClearCrossCrate::Clear,
ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v),
}
}
pub fn unwrap_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) {
if E::CLEAR_CROSS_CRATE {
return;
}
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) -> ClearCrossCrate<T> {
if D::CLEAR_CROSS_CRATE {
return ClearCrossCrate::Clear;
}
let discr = u8::decode(d);
match discr {
TAG_CLEAR_CROSS_CRATE_CLEAR => ClearCrossCrate::Clear,
TAG_CLEAR_CROSS_CRATE_SET => {
let val = T::decode(d);
ClearCrossCrate::Set(val)
}
tag => panic!("Invalid tag for ClearCrossCrate: {tag:?}"),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash, StableHash)]
pub struct SourceInfo {
pub span: Span,
pub scope: SourceScope,
}
impl SourceInfo {
#[inline]
pub fn outermost(span: Span) -> Self {
SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE }
}
}
crate::rustc_index::newtype_index! {
#[stable_hash]
#[encodable]
#[orderable]
#[debug_format = "_{}"]
pub struct Local {
const RETURN_PLACE = 0;
}
}
impl Local {
pub const fn arg(i: usize) -> Local {
Local::from_usize(i + 1)
}
}
impl Atom for Local {
fn index(self) -> usize {
Idx::index(self)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, StableHash)]
pub enum LocalKind {
Temp,
Arg,
ReturnPointer,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash)]
pub struct VarBindingForm<'tcx> {
pub binding_mode: BindingMode,
pub opt_ty_info: Option<Span>,
pub opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
pub pat_span: Span,
pub introductions: Vec<VarBindingIntroduction>,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash)]
pub enum BindingForm<'tcx> {
Var(VarBindingForm<'tcx>),
ImplicitSelf(ImplicitSelfKind),
RefForGuard(Local),
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash)]
pub struct VarBindingIntroduction {
pub span: Span,
pub is_shorthand: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, StableHash)]
pub struct BlockTailInfo {
pub tail_result_is_ignored: bool,
pub span: Span,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)]
pub struct LocalDecl<'tcx> {
pub mutability: Mutability,
pub local_info: ClearCrossCrate<Box<LocalInfo<'tcx>>>,
pub ty: Ty<'tcx>,
pub user_ty: Option<Box<UserTypeProjections>>,
pub source_info: SourceInfo,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)]
pub enum LocalInfo<'tcx> {
User(BindingForm<'tcx>),
StaticRef { def_id: DefId, is_thread_local: bool },
ConstRef { def_id: DefId },
AggregateTemp,
BlockTailTemp(BlockTailInfo),
IfThenRescopeTemp { if_then: HirId },
DerefTemp,
FakeBorrow,
Boring,
}
impl<'tcx> LocalDecl<'tcx> {
pub fn local_info(&self) -> &LocalInfo<'tcx> {
self.local_info.as_ref().unwrap_crate_local()
}
pub fn can_be_made_mutable(&self) -> bool {
matches!(
self.local_info(),
LocalInfo::User(
BindingForm::Var(VarBindingForm { binding_mode: BindingMode(ByRef::No, _), .. })
| BindingForm::ImplicitSelf(ImplicitSelfKind::Imm),
)
)
}
pub fn is_nonref_binding(&self) -> bool {
matches!(
self.local_info(),
LocalInfo::User(
BindingForm::Var(VarBindingForm { binding_mode: BindingMode(ByRef::No, _), .. })
| BindingForm::ImplicitSelf(_),
)
)
}
#[inline]
pub fn is_user_variable(&self) -> bool {
matches!(self.local_info(), LocalInfo::User(_))
}
pub fn is_ref_for_guard(&self) -> bool {
matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard(_)))
}
pub fn is_ref_to_static(&self) -> bool {
matches!(self.local_info(), LocalInfo::StaticRef { .. })
}
pub fn is_ref_to_thread_local(&self) -> bool {
match self.local_info() {
LocalInfo::StaticRef { is_thread_local, .. } => *is_thread_local,
_ => false,
}
}
pub fn is_deref_temp(&self) -> bool {
match self.local_info() {
LocalInfo::DerefTemp => true,
_ => 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: ClearCrossCrate::Set(Box::new(LocalInfo::Boring)),
ty,
user_ty: None,
source_info,
}
}
#[inline]
pub fn immutable(mut self) -> Self {
self.mutability = Mutability::Not;
self
}
}
#[derive(Clone, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)]
pub enum VarDebugInfoContents<'tcx> {
Place(Place<'tcx>),
Const(ConstOperand<'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, StableHash, TypeFoldable, TypeVisitable)]
pub struct VarDebugInfoFragment<'tcx> {
pub ty: Ty<'tcx>,
pub projection: Vec<PlaceElem<'tcx>>,
}
#[derive(Clone, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)]
pub struct VarDebugInfo<'tcx> {
pub name: Symbol,
pub source_info: SourceInfo,
pub composite: Option<Box<VarDebugInfoFragment<'tcx>>>,
pub value: VarDebugInfoContents<'tcx>,
pub argument_index: Option<u16>,
}
crate::rustc_index::newtype_index! {
#[stable_hash]
#[encodable]
#[orderable]
#[debug_format = "bb{}"]
pub struct BasicBlock {
const START_BLOCK = 0;
}
}
impl BasicBlock {
pub fn start_location(self) -> Location {
Location { block: self, statement_index: 0 }
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)]
#[non_exhaustive]
pub struct BasicBlockData<'tcx> {
pub statements: Vec<Statement<'tcx>>,
pub after_last_stmt_debuginfos: StmtDebugInfos<'tcx>,
pub terminator: Option<Terminator<'tcx>>,
pub is_cleanup: bool,
}
impl<'tcx> BasicBlockData<'tcx> {
pub fn new(terminator: Option<Terminator<'tcx>>, is_cleanup: bool) -> BasicBlockData<'tcx> {
BasicBlockData::new_stmts(Vec::new(), terminator, is_cleanup)
}
pub fn new_stmts(
statements: Vec<Statement<'tcx>>,
terminator: Option<Terminator<'tcx>>,
is_cleanup: bool,
) -> BasicBlockData<'tcx> {
BasicBlockData {
statements,
after_last_stmt_debuginfos: StmtDebugInfos::default(),
terminator,
is_cleanup,
}
}
#[inline]
pub fn terminator(&self) -> &Terminator<'tcx> {
self.terminator.as_ref().expect("invalid terminator state")
}
#[inline]
pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> {
self.terminator.as_mut().expect("invalid terminator state")
}
#[inline]
pub fn is_empty_unreachable(&self) -> bool {
self.statements.is_empty() && matches!(self.terminator().kind, TerminatorKind::Unreachable)
}
pub fn mono_successors(&self, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Successors<'_> {
if let Some((bits, targets)) = Body::try_const_mono_switchint(tcx, instance, self) {
targets.successors_for_value(bits)
} else {
self.terminator().successors()
}
}
pub fn retain_statements<F>(&mut self, mut f: F)
where
F: FnMut(&Statement<'tcx>) -> bool,
{
let mut debuginfos = StmtDebugInfos::default();
self.statements.retain_mut(|stmt| {
let retain = f(stmt);
if retain {
stmt.debuginfos.prepend(&mut debuginfos);
} else {
debuginfos.append(&mut stmt.debuginfos);
}
retain
});
self.after_last_stmt_debuginfos.prepend(&mut debuginfos);
}
pub fn strip_nops(&mut self) {
self.retain_statements(|stmt| !matches!(stmt.kind, StatementKind::Nop))
}
pub fn drop_debuginfo(&mut self) {
self.after_last_stmt_debuginfos.drop_debuginfo();
for stmt in self.statements.iter_mut() {
stmt.debuginfos.drop_debuginfo();
}
}
}
crate::rustc_index::newtype_index! {
#[stable_hash]
#[encodable]
#[debug_format = "scope[{}]"]
pub struct SourceScope {
const OUTERMOST_SOURCE_SCOPE = 0;
}
}
impl SourceScope {
pub fn lint_root(
self,
source_scopes: &IndexSlice<SourceScope, SourceScopeData<'_>>,
) -> Option<HirId> {
let mut data = &source_scopes[self];
while data.inlined.is_some() {
trace!(?data);
data = &source_scopes[data.parent_scope.unwrap()];
}
trace!(?data);
match &data.local_data {
ClearCrossCrate::Set(data) => Some(data.lint_root),
ClearCrossCrate::Clear => None,
}
}
#[inline]
pub fn inlined_instance<'tcx>(
self,
source_scopes: &IndexSlice<SourceScope, SourceScopeData<'tcx>>,
) -> Option<ty::Instance<'tcx>> {
let scope_data = &source_scopes[self];
if let Some((inlined_instance, _)) = scope_data.inlined {
Some(inlined_instance)
} else if let Some(inlined_scope) = scope_data.inlined_parent_scope {
Some(source_scopes[inlined_scope].inlined.unwrap().0)
} else {
None
}
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)]
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, StableHash)]
pub struct SourceScopeLocalData {
pub lint_root: HirId,
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash, TypeFoldable, TypeVisitable)]
pub struct UserTypeProjections {
pub contents: Vec<UserTypeProjection>,
}
impl UserTypeProjections {
pub fn projections(&self) -> impl Iterator<Item = &UserTypeProjection> + ExactSizeIterator {
self.contents.iter()
}
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, StableHash, PartialEq)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct UserTypeProjection {
pub base: UserTypeAnnotationIndex,
pub projs: Vec<ProjectionKind>,
}
crate::rustc_index::newtype_index! {
#[stable_hash]
#[encodable]
#[orderable]
#[debug_format = "promoted[{}]"]
pub struct Promoted {}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, StableHash)]
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 };
#[inline]
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.basic_blocks.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
}
#[inline]
pub fn dominates(&self, other: Location, dominators: &Dominators<BasicBlock>) -> bool {
if self.block == other.block {
self.statement_index <= other.statement_index
} else {
dominators.dominates(self.block, other.block)
}
}
#[inline]
pub fn strictly_dominates(&self, other: Location, dominators: &Dominators<BasicBlock>) -> bool {
self.block != other.block && dominators.strictly_dominates(self.block, other.block)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DefLocation {
Argument,
Assignment(Location),
CallReturn { call: BasicBlock, target: Option<BasicBlock> },
}
impl DefLocation {
#[inline]
pub fn dominates(self, location: Location, dominators: &Dominators<BasicBlock>) -> bool {
match self {
DefLocation::Argument => true,
DefLocation::Assignment(def) => {
def.successor_within_block().dominates(location, dominators)
}
DefLocation::CallReturn { target: None, .. } => false,
DefLocation::CallReturn { call, target: Some(target) } => {
call != target
&& dominators.dominates(call, target)
&& dominators.dominates(target, location.block)
}
}
}
}
pub fn find_self_call<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
local: Local,
block: BasicBlock,
) -> Option<(DefId, GenericArgsRef<'tcx>)> {
debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator);
if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
&body[block].terminator
&& let Operand::Constant(constant) = func
&& let ConstOperand { const_, .. } = &**constant
&& let ty::FnDef(def_id, fn_args) = *const_.ty().kind()
&& let Some(item) = tcx.opt_associated_item(def_id)
&& item.is_method()
&& let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] =
**args
{
let fn_args = fn_args.no_bound_vars().unwrap();
if self_place.as_local() == Some(local) {
return Some((def_id, fn_args));
}
for stmt in &body[block].statements {
if let StatementKind::Assign(assign) = &stmt.kind
&& let (place, rvalue) = &**assign
&& let Some(reborrow_local) = place.as_local()
&& self_place.as_local() == Some(reborrow_local)
&& let Rvalue::Ref(_, _, deref_place) = rvalue
&& let PlaceRef { local: deref_local, projection: [ProjectionElem::Deref] } =
deref_place.as_ref()
&& deref_local == local
{
return Some((def_id, fn_args));
}
}
}
None
}
#[cfg(target_pointer_width = "64")]
mod size_asserts {
use crate::static_assert_size;
use super::*;
static_assert_size!(BasicBlockData<'_>, 152);
static_assert_size!(LocalDecl<'_>, 40);
static_assert_size!(SourceScopeData<'_>, 88);
static_assert_size!(Statement<'_>, 40);
static_assert_size!(Terminator<'_>, 112);
static_assert_size!(VarDebugInfo<'_>, 88);
}