1use rustc_ast::MetaItem;
2use rustc_middle::mir::{self, Body, Local, Location};
3use rustc_middle::ty::{self, Ty, TyCtxt};
4use rustc_span::def_id::DefId;
5use rustc_span::{Span, Symbol, sym};
6use tracing::{debug, info};
7
8use crate::errors::{
9 PeekArgumentNotALocal, PeekArgumentUntracked, PeekBitNotSet, PeekMustBeNotTemporary,
10 PeekMustBePlaceOrRefPlace, StopAfterDataFlowEndedCompilation,
11};
12use crate::framework::BitSetExt;
13use crate::impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces};
14use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
15use crate::{Analysis, JoinSemiLattice, ResultsCursor};
16
17fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option<MetaItem> {
18 for attr in tcx.get_attrs(def_id, sym::rustc_mir) {
19 let items = attr.meta_item_list();
20 for item in items.iter().flat_map(|l| l.iter()) {
21 match item.meta_item() {
22 Some(mi) if mi.has_name(name) => return Some(mi.clone()),
23 _ => continue,
24 }
25 }
26 }
27 None
28}
29
30pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
31 let def_id = body.source.def_id();
32 if !tcx.has_attr(def_id, sym::rustc_mir) {
33 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_dataflow/src/rustc_peek.rs:33",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(33u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::rustc_peek"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("skipping rustc_peek::SanityCheck on {0}",
tcx.def_path_str(def_id)) as &dyn Value))])
});
} else { ; }
};debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
34 return;
35 } else {
36 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_dataflow/src/rustc_peek.rs:36",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(36u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::rustc_peek"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("running rustc_peek::SanityCheck on {0}",
tcx.def_path_str(def_id)) as &dyn Value))])
});
} else { ; }
};debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
37 }
38
39 let move_data = MoveData::gather_moves(body, tcx, |_| true);
40
41 if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
42 let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
43 .iterate_to_fixpoint(tcx, body, None)
44 .into_results_cursor(body);
45 sanity_check_via_rustc_peek(tcx, flow_inits);
46 }
47
48 if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() {
49 let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data)
50 .iterate_to_fixpoint(tcx, body, None)
51 .into_results_cursor(body);
52 sanity_check_via_rustc_peek(tcx, flow_uninits);
53 }
54
55 if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
56 let flow_liveness =
57 MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
58 sanity_check_via_rustc_peek(tcx, flow_liveness);
59 }
60
61 if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
62 tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation);
63 }
64}
65
66fn sanity_check_via_rustc_peek<'tcx, A>(tcx: TyCtxt<'tcx>, mut cursor: ResultsCursor<'_, 'tcx, A>)
83where
84 A: RustcPeekAt<'tcx>,
85{
86 let def_id = cursor.body().source.def_id();
87 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_dataflow/src/rustc_peek.rs:87",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(87u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::rustc_peek"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("sanity_check_via_rustc_peek def_id: {0:?}",
def_id) as &dyn Value))])
});
} else { ; }
};debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
88
89 let peek_calls = cursor.body().basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| {
90 PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
91 });
92
93 for (bb, block_data, call) in peek_calls {
94 let (statement_index, peek_rval) = block_data
103 .statements
104 .iter()
105 .enumerate()
106 .find_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
107 .expect(
108 "call to rustc_peek should be preceded by \
109 assignment to temporary holding its argument",
110 );
111
112 match (call.kind, peek_rval) {
113 (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
114 | (
115 PeekCallKind::ByVal,
116 mir::Rvalue::Use(mir::Operand::Move(place) | mir::Operand::Copy(place)),
117 ) => {
118 let loc = Location { block: bb, statement_index };
119 cursor.seek_before_primary_effect(loc);
120 let state = cursor.get();
121 let analysis = cursor.analysis();
122 analysis.peek_at(tcx, *place, state, call);
123 }
124
125 _ => {
126 tcx.dcx().emit_err(PeekMustBePlaceOrRefPlace { span: call.span });
127 }
128 }
129 }
130}
131
132fn value_assigned_to_local<'a, 'tcx>(
135 stmt: &'a mir::Statement<'tcx>,
136 local: Local,
137) -> Option<&'a mir::Rvalue<'tcx>> {
138 if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind
139 && let Some(l) = place.as_local()
140 && local == l
141 {
142 return Some(&*rvalue);
143 }
144
145 None
146}
147
148#[derive(#[automatically_derived]
impl ::core::clone::Clone for PeekCallKind {
#[inline]
fn clone(&self) -> PeekCallKind { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for PeekCallKind { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for PeekCallKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
PeekCallKind::ByVal => "ByVal",
PeekCallKind::ByRef => "ByRef",
})
}
}Debug)]
149enum PeekCallKind {
150 ByVal,
151 ByRef,
152}
153
154impl PeekCallKind {
155 fn from_arg_ty(arg: Ty<'_>) -> Self {
156 match arg.kind() {
157 ty::Ref(_, _, _) => PeekCallKind::ByRef,
158 _ => PeekCallKind::ByVal,
159 }
160 }
161}
162
163#[derive(#[automatically_derived]
impl ::core::clone::Clone for PeekCall {
#[inline]
fn clone(&self) -> PeekCall {
let _: ::core::clone::AssertParamIsClone<Local>;
let _: ::core::clone::AssertParamIsClone<PeekCallKind>;
let _: ::core::clone::AssertParamIsClone<Span>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for PeekCall { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for PeekCall {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "PeekCall",
"arg", &self.arg, "kind", &self.kind, "span", &&self.span)
}
}Debug)]
164struct PeekCall {
165 arg: Local,
166 kind: PeekCallKind,
167 span: Span,
168}
169
170impl PeekCall {
171 fn from_terminator<'tcx>(
172 tcx: TyCtxt<'tcx>,
173 terminator: &mir::Terminator<'tcx>,
174 ) -> Option<Self> {
175 use mir::Operand;
176
177 let span = terminator.source_info.span;
178 if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
179 &terminator.kind
180 && let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind()
181 {
182 if tcx.intrinsic(def_id)?.name != sym::rustc_peek {
183 return None;
184 }
185
186 match (&fn_args.len(), &1) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(fn_args.len(), 1);
187 let kind = PeekCallKind::from_arg_ty(fn_args.type_at(0));
188 let arg = match &args[0].node {
189 Operand::Copy(place) | Operand::Move(place) => {
190 if let Some(local) = place.as_local() {
191 local
192 } else {
193 tcx.dcx().emit_err(PeekMustBeNotTemporary { span });
194 return None;
195 }
196 }
197 _ => {
198 tcx.dcx().emit_err(PeekMustBeNotTemporary { span });
199 return None;
200 }
201 };
202
203 return Some(PeekCall { arg, kind, span });
204 }
205
206 None
207 }
208}
209
210trait RustcPeekAt<'tcx>: Analysis<'tcx> {
211 fn peek_at(
212 &self,
213 tcx: TyCtxt<'tcx>,
214 place: mir::Place<'tcx>,
215 state: &Self::Domain,
216 call: PeekCall,
217 );
218}
219
220impl<'tcx, A, D> RustcPeekAt<'tcx> for A
221where
222 A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>,
223 D: JoinSemiLattice + Clone + BitSetExt<MovePathIndex>,
224{
225 fn peek_at(
226 &self,
227 tcx: TyCtxt<'tcx>,
228 place: mir::Place<'tcx>,
229 state: &Self::Domain,
230 call: PeekCall,
231 ) {
232 match self.move_data().rev_lookup.find(place.as_ref()) {
233 LookupResult::Exact(peek_mpi) => {
234 let bit_state = state.contains(peek_mpi);
235 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_dataflow/src/rustc_peek.rs:235",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(235u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::rustc_peek"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("rustc_peek({0:?} = &{1:?}) bit_state: {2}",
call.arg, place, bit_state) as &dyn Value))])
});
} else { ; }
};debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
236 if !bit_state {
237 tcx.dcx().emit_err(PeekBitNotSet { span: call.span });
238 }
239 }
240
241 LookupResult::Parent(..) => {
242 tcx.dcx().emit_err(PeekArgumentUntracked { span: call.span });
243 }
244 }
245 }
246}
247
248impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
249 fn peek_at(
250 &self,
251 tcx: TyCtxt<'tcx>,
252 place: mir::Place<'tcx>,
253 state: &Self::Domain,
254 call: PeekCall,
255 ) {
256 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_dataflow/src/rustc_peek.rs:256",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(256u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::rustc_peek"),
::tracing_core::field::FieldSet::new(&["message", "place"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("peek_at")
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&place) as
&dyn Value))])
});
} else { ; }
};info!(?place, "peek_at");
257 let Some(local) = place.as_local() else {
258 tcx.dcx().emit_err(PeekArgumentNotALocal { span: call.span });
259 return;
260 };
261
262 if !state.contains(local) {
263 tcx.dcx().emit_err(PeekBitNotSet { span: call.span });
264 }
265 }
266}