1use rustc_attr_ir::{RustcMirKind, find_attr};
2use rustc_middle::mir::{self, Body, Local, Location};
3use rustc_middle::ty::{self, Ty, TyCtxt};
4use rustc_span::{Span, sym};
5use tracing::{debug, info};
6
7use crate::diagnostics::{
8 PeekArgumentNotALocal, PeekArgumentUntracked, PeekBitNotSet, PeekMustBeNotTemporary,
9 PeekMustBePlaceOrRefPlace, StopAfterDataFlowEndedCompilation,
10};
11use crate::framework::BitSetExt;
12use crate::impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces};
13use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
14use crate::{Analysis, JoinSemiLattice, ResultsCursor};
15
16pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
17 let def_id = body.source.def_id();
18 if let Some(kind) = {
{
'done:
{
for i in ::rustc_attr_ir::HasAttrs::get_attrs(def_id, &tcx) {
#[allow(unused_imports)]
use ::rustc_attr_ir::AttributeKind::*;
let i: &::rustc_attr_ir::Attribute = i;
match i {
::rustc_attr_ir::Attribute::Parsed(RustcMir(kind)) => {
break 'done Some(kind);
}
::rustc_attr_ir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}find_attr!(tcx, def_id, RustcMir(kind) => kind) {
19 let move_data = MoveData::gather_moves(body, tcx, |_| true);
20 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs:20",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(20u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("running rustc_peek::SanityCheck on {0}",
tcx.def_path_str(def_id)) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
21 if kind.contains(&RustcMirKind::PeekMaybeInit) {
22 let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
23 .iterate_to_fixpoint(tcx, body, None)
24 .into_results_cursor(body);
25 sanity_check_via_rustc_peek(tcx, flow_inits);
26 }
27
28 if kind.contains(&RustcMirKind::PeekMaybeUninit) {
29 let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data)
30 .iterate_to_fixpoint(tcx, body, None)
31 .into_results_cursor(body);
32 sanity_check_via_rustc_peek(tcx, flow_uninits);
33 }
34
35 if kind.contains(&RustcMirKind::PeekLiveness) {
36 let flow_liveness =
37 MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
38 sanity_check_via_rustc_peek(tcx, flow_liveness);
39 }
40
41 if kind.contains(&RustcMirKind::StopAfterDataflow) {
42 tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation);
43 }
44 } else {
45 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs:45",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(45u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("skipping rustc_peek::SanityCheck on {0}",
tcx.def_path_str(def_id)) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
46 }
47}
48
49fn sanity_check_via_rustc_peek<'tcx, A>(tcx: TyCtxt<'tcx>, mut cursor: ResultsCursor<'_, 'tcx, A>)
66where
67 A: RustcPeekAt<'tcx>,
68{
69 let def_id = cursor.body().source.def_id();
70 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs:70",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(70u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("sanity_check_via_rustc_peek def_id: {0:?}",
def_id) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
71
72 let peek_calls = cursor.body().basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| {
73 PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
74 });
75
76 for (bb, block_data, call) in peek_calls {
77 let (statement_index, peek_rval) = block_data
86 .statements
87 .iter()
88 .enumerate()
89 .find_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
90 .expect(
91 "call to rustc_peek should be preceded by \
92 assignment to temporary holding its argument",
93 );
94
95 match (call.kind, peek_rval) {
96 (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
97 | (
98 PeekCallKind::ByVal,
99 mir::Rvalue::Use(mir::Operand::Move(place) | mir::Operand::Copy(place), _),
100 ) => {
101 let loc = Location { block: bb, statement_index };
102 cursor.seek_before_primary_effect(loc);
103 let state = cursor.get();
104 let analysis = cursor.analysis();
105 analysis.peek_at(tcx, *place, state, call);
106 }
107
108 _ => {
109 tcx.dcx().emit_err(PeekMustBePlaceOrRefPlace { span: call.span });
110 }
111 }
112 }
113}
114
115fn value_assigned_to_local<'a, 'tcx>(
118 stmt: &'a mir::Statement<'tcx>,
119 local: Local,
120) -> Option<&'a mir::Rvalue<'tcx>> {
121 if let mir::StatementKind::Assign((place, rvalue)) = &stmt.kind
122 && let Some(l) = place.as_local()
123 && local == l
124 {
125 return Some(&*rvalue);
126 }
127
128 None
129}
130
131#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for PeekCallKind { }
#[automatically_derived]
impl ::core::clone::Clone for PeekCallKind {
#[inline]
fn clone(&self) -> Self { *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)]
132enum PeekCallKind {
133 ByVal,
134 ByRef,
135}
136
137impl PeekCallKind {
138 fn from_arg_ty(arg: Ty<'_>) -> Self {
139 match arg.kind() {
140 ty::Ref(_, _, _) => PeekCallKind::ByRef,
141 _ => PeekCallKind::ByVal,
142 }
143 }
144}
145
146#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for PeekCall { }
#[automatically_derived]
impl ::core::clone::Clone for PeekCall {
#[inline]
fn clone(&self) -> Self {
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)]
147struct PeekCall {
148 arg: Local,
149 kind: PeekCallKind,
150 span: Span,
151}
152
153impl PeekCall {
154 fn from_terminator<'tcx>(
155 tcx: TyCtxt<'tcx>,
156 terminator: &mir::Terminator<'tcx>,
157 ) -> Option<Self> {
158 use mir::Operand;
159
160 let span = terminator.source_info.span;
161 if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
162 &terminator.kind
163 && let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind()
164 {
165 let fn_args = fn_args.no_bound_vars().unwrap();
166
167 if tcx.intrinsic(def_id)?.name != sym::rustc_peek {
168 return None;
169 }
170
171 {
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);
172 let kind = PeekCallKind::from_arg_ty(fn_args.type_at(0));
173 let arg = match &args[0].node {
174 Operand::Copy(place) | Operand::Move(place) => {
175 if let Some(local) = place.as_local() {
176 local
177 } else {
178 tcx.dcx().emit_err(PeekMustBeNotTemporary { span });
179 return None;
180 }
181 }
182 _ => {
183 tcx.dcx().emit_err(PeekMustBeNotTemporary { span });
184 return None;
185 }
186 };
187
188 return Some(PeekCall { arg, kind, span });
189 }
190
191 None
192 }
193}
194
195trait RustcPeekAt<'tcx>: Analysis<'tcx> {
196 fn peek_at(
197 &self,
198 tcx: TyCtxt<'tcx>,
199 place: mir::Place<'tcx>,
200 state: &Self::Domain,
201 call: PeekCall,
202 );
203}
204
205impl<'tcx, A, D> RustcPeekAt<'tcx> for A
206where
207 A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>,
208 D: JoinSemiLattice + Clone + BitSetExt<MovePathIndex>,
209{
210 fn peek_at(
211 &self,
212 tcx: TyCtxt<'tcx>,
213 place: mir::Place<'tcx>,
214 state: &Self::Domain,
215 call: PeekCall,
216 ) {
217 match self.move_data().rev_lookup.find(place.as_ref()) {
218 LookupResult::Exact(peek_mpi) => {
219 let bit_state = state.contains(peek_mpi);
220 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs:220",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(220u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("rustc_peek({0:?} = &{1:?}) bit_state: {2}",
call.arg, place, bit_state) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
221 if !bit_state {
222 tcx.dcx().emit_err(PeekBitNotSet { span: call.span });
223 }
224 }
225
226 LookupResult::Parent(..) => {
227 tcx.dcx().emit_err(PeekArgumentUntracked { span: call.span });
228 }
229 }
230 }
231}
232
233impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
234 fn peek_at(
235 &self,
236 tcx: TyCtxt<'tcx>,
237 place: mir::Place<'tcx>,
238 state: &Self::Domain,
239 call: PeekCall,
240 ) {
241 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs:241",
"rustc_mir_dataflow::rustc_peek", ::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("/rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_mir_dataflow/src/rustc_peek.rs"),
::tracing_core::__macro_support::Option::Some(241u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::rustc_peek"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("place")
}> =
::tracing::__macro_support::FieldName::new("place");
NAME.as_str()
}], ::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("peek_at")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&place)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};info!(?place, "peek_at");
242 let Some(local) = place.as_local() else {
243 tcx.dcx().emit_err(PeekArgumentNotALocal { span: call.span });
244 return;
245 };
246
247 if !state.contains(local) {
248 tcx.dcx().emit_err(PeekBitNotSet { span: call.span });
249 }
250 }
251}