2020-01-01 18:30:57 +00:00
|
|
|
use rustc_span::symbol::sym;
|
2019-12-31 17:15:40 +00:00
|
|
|
use rustc_span::Span;
|
2016-05-11 20:03:57 +00:00
|
|
|
|
2022-06-05 23:22:54 +00:00
|
|
|
use rustc_index::bit_set::ChunkedBitSet;
|
2021-01-05 18:53:07 +00:00
|
|
|
use rustc_middle::mir::MirPass;
|
2020-04-12 17:31:00 +00:00
|
|
|
use rustc_middle::mir::{self, Body, Local, Location};
|
2020-03-29 15:19:48 +00:00
|
|
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
2016-05-11 20:03:57 +00:00
|
|
|
|
2022-08-19 09:21:14 +00:00
|
|
|
use crate::errors::{
|
|
|
|
PeekArgumentNotALocal, PeekArgumentUntracked, PeekBitNotSet, PeekMustBeNotTemporary,
|
|
|
|
PeekMustBePlaceOrRefPlace, StopAfterDataFlowEndedCompilation,
|
|
|
|
};
|
2022-02-09 13:47:48 +00:00
|
|
|
use crate::framework::BitSetExt;
|
2021-01-05 18:53:07 +00:00
|
|
|
use crate::impls::{
|
2021-10-29 00:00:00 +00:00
|
|
|
DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces,
|
2020-05-04 19:50:36 +00:00
|
|
|
};
|
2021-01-05 18:53:07 +00:00
|
|
|
use crate::move_paths::{HasMoveData, MoveData};
|
|
|
|
use crate::move_paths::{LookupResult, MovePathIndex};
|
|
|
|
use crate::MoveDataParamEnv;
|
2023-02-20 22:28:03 +00:00
|
|
|
use crate::{Analysis, JoinSemiLattice, ResultsCursor};
|
2017-06-26 12:57:26 +00:00
|
|
|
|
|
|
|
pub struct SanityCheck;
|
|
|
|
|
2021-12-02 17:17:32 +00:00
|
|
|
// FIXME: This should be a `MirLint`, but it needs to be moved back to `rustc_mir_transform` first.
|
2019-08-04 20:20:00 +00:00
|
|
|
impl<'tcx> MirPass<'tcx> for SanityCheck {
|
2020-10-04 18:01:38 +00:00
|
|
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
2021-01-05 18:53:07 +00:00
|
|
|
use crate::has_rustc_mir_with;
|
2020-10-04 18:01:38 +00:00
|
|
|
let def_id = body.source.def_id();
|
2019-05-08 03:21:18 +00:00
|
|
|
if !tcx.has_attr(def_id, sym::rustc_mir) {
|
2018-12-19 10:31:35 +00:00
|
|
|
debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
|
2017-06-26 12:57:26 +00:00
|
|
|
return;
|
|
|
|
} else {
|
2018-12-19 10:31:35 +00:00
|
|
|
debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
|
2017-06-26 12:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let param_env = tcx.param_env(def_id);
|
2023-06-21 07:39:13 +00:00
|
|
|
let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
|
2022-07-28 09:56:57 +00:00
|
|
|
let mdpe = MoveDataParamEnv { move_data, param_env };
|
2020-01-20 23:16:24 +00:00
|
|
|
|
2022-05-02 07:31:56 +00:00
|
|
|
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
|
2020-04-10 18:00:11 +00:00
|
|
|
let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
|
2020-10-04 22:22:23 +00:00
|
|
|
.into_engine(tcx, body)
|
2020-04-10 18:00:11 +00:00
|
|
|
.iterate_to_fixpoint();
|
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body));
|
2017-06-26 12:57:26 +00:00
|
|
|
}
|
2020-04-10 18:00:11 +00:00
|
|
|
|
2022-05-02 07:31:56 +00:00
|
|
|
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() {
|
2020-04-10 18:00:11 +00:00
|
|
|
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe)
|
2020-10-04 22:22:23 +00:00
|
|
|
.into_engine(tcx, body)
|
2020-04-10 18:00:11 +00:00
|
|
|
.iterate_to_fixpoint();
|
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body));
|
2017-06-26 12:57:26 +00:00
|
|
|
}
|
2020-04-10 18:00:11 +00:00
|
|
|
|
2022-05-02 07:31:56 +00:00
|
|
|
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() {
|
2020-04-10 18:00:11 +00:00
|
|
|
let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe)
|
2020-10-04 22:22:23 +00:00
|
|
|
.into_engine(tcx, body)
|
2020-04-10 18:00:11 +00:00
|
|
|
.iterate_to_fixpoint();
|
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body));
|
2017-06-26 12:57:26 +00:00
|
|
|
}
|
2020-04-10 18:00:11 +00:00
|
|
|
|
2022-05-02 07:31:56 +00:00
|
|
|
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
|
2020-10-04 22:22:23 +00:00
|
|
|
let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint();
|
2020-04-10 18:00:11 +00:00
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body));
|
2020-04-10 18:00:11 +00:00
|
|
|
}
|
|
|
|
|
2022-05-02 07:31:56 +00:00
|
|
|
if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_fatal(StopAfterDataFlowEndedCompilation);
|
2017-06-26 12:57:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-05-11 20:03:57 +00:00
|
|
|
|
2016-05-17 06:37:36 +00:00
|
|
|
/// This function scans `mir` for all calls to the intrinsic
|
|
|
|
/// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
|
|
|
|
///
|
|
|
|
/// For each such call, determines what the dataflow bit-state is for
|
|
|
|
/// the L-value corresponding to `expr`; if the bit-state is a 1, then
|
|
|
|
/// that call to `rustc_peek` is ignored by the sanity check. If the
|
2021-08-22 12:46:15 +00:00
|
|
|
/// bit-state is a 0, then this pass emits an error message saying
|
2016-05-17 06:37:36 +00:00
|
|
|
/// "rustc_peek: bit not set".
|
|
|
|
///
|
|
|
|
/// The intention is that one can write unit tests for dataflow by
|
2021-08-22 16:15:49 +00:00
|
|
|
/// putting code into a UI test and using `rustc_peek` to
|
2016-05-17 06:37:36 +00:00
|
|
|
/// make observations about the results of dataflow static analyses.
|
|
|
|
///
|
|
|
|
/// (If there are any calls to `rustc_peek` that do not match the
|
|
|
|
/// expression form above, then that emits an error as well, but those
|
|
|
|
/// errors are not intended to be used for unit tests.)
|
2020-01-20 23:16:24 +00:00
|
|
|
pub fn sanity_check_via_rustc_peek<'tcx, A>(
|
2019-06-13 21:48:52 +00:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2023-02-20 22:28:03 +00:00
|
|
|
mut cursor: ResultsCursor<'_, 'tcx, A>,
|
2019-07-06 01:18:38 +00:00
|
|
|
) where
|
2020-01-20 23:16:24 +00:00
|
|
|
A: RustcPeekAt<'tcx>,
|
2019-07-06 01:18:38 +00:00
|
|
|
{
|
2023-02-20 22:28:03 +00:00
|
|
|
let def_id = cursor.body().source.def_id();
|
2019-03-17 10:36:10 +00:00
|
|
|
debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
|
2016-05-11 20:03:57 +00:00
|
|
|
|
2023-02-20 22:28:03 +00:00
|
|
|
let peek_calls = cursor.body().basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| {
|
2019-07-06 01:18:38 +00:00
|
|
|
PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
|
|
|
|
});
|
|
|
|
|
|
|
|
for (bb, block_data, call) in peek_calls {
|
|
|
|
// Look for a sequence like the following to indicate that we should be peeking at `_1`:
|
|
|
|
// _2 = &_1;
|
|
|
|
// rustc_peek(_2);
|
|
|
|
//
|
|
|
|
// /* or */
|
|
|
|
//
|
|
|
|
// _2 = _1;
|
|
|
|
// rustc_peek(_2);
|
|
|
|
let (statement_index, peek_rval) = block_data
|
|
|
|
.statements
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2020-04-25 03:03:45 +00:00
|
|
|
.find_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
|
2019-07-06 01:18:38 +00:00
|
|
|
.expect(
|
|
|
|
"call to rustc_peek should be preceded by \
|
|
|
|
assignment to temporary holding its argument",
|
|
|
|
);
|
|
|
|
|
|
|
|
match (call.kind, peek_rval) {
|
|
|
|
(PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
|
2020-04-17 00:38:52 +00:00
|
|
|
| (
|
|
|
|
PeekCallKind::ByVal,
|
|
|
|
mir::Rvalue::Use(mir::Operand::Move(place) | mir::Operand::Copy(place)),
|
|
|
|
) => {
|
2019-07-06 01:18:38 +00:00
|
|
|
let loc = Location { block: bb, statement_index };
|
2020-03-22 19:09:40 +00:00
|
|
|
cursor.seek_before_primary_effect(loc);
|
2023-02-20 22:28:03 +00:00
|
|
|
let (state, analysis) = cursor.get_with_analysis();
|
|
|
|
analysis.peek_at(tcx, *place, state, call);
|
2016-05-24 10:56:02 +00:00
|
|
|
}
|
2019-07-06 01:18:38 +00:00
|
|
|
|
|
|
|
_ => {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(PeekMustBePlaceOrRefPlace { span: call.span });
|
2016-05-24 10:56:02 +00:00
|
|
|
}
|
2016-05-11 20:03:57 +00:00
|
|
|
}
|
2019-07-06 01:18:38 +00:00
|
|
|
}
|
|
|
|
}
|
2016-05-24 21:03:52 +00:00
|
|
|
|
2019-07-06 01:18:38 +00:00
|
|
|
/// If `stmt` is an assignment where the LHS is the given local (with no projections), returns the
|
|
|
|
/// RHS of the assignment.
|
|
|
|
fn value_assigned_to_local<'a, 'tcx>(
|
|
|
|
stmt: &'a mir::Statement<'tcx>,
|
|
|
|
local: Local,
|
|
|
|
) -> Option<&'a mir::Rvalue<'tcx>> {
|
|
|
|
if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
|
2019-10-20 20:09:36 +00:00
|
|
|
if let Some(l) = place.as_local() {
|
|
|
|
if local == l {
|
2019-07-06 01:18:38 +00:00
|
|
|
return Some(&*rvalue);
|
|
|
|
}
|
|
|
|
}
|
2016-05-11 20:03:57 +00:00
|
|
|
}
|
2016-05-20 11:18:03 +00:00
|
|
|
|
2019-07-06 01:18:38 +00:00
|
|
|
None
|
|
|
|
}
|
2017-12-23 22:45:53 +00:00
|
|
|
|
2019-07-06 01:18:38 +00:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
enum PeekCallKind {
|
|
|
|
ByVal,
|
|
|
|
ByRef,
|
2016-05-20 11:18:03 +00:00
|
|
|
}
|
|
|
|
|
2019-07-06 01:18:38 +00:00
|
|
|
impl PeekCallKind {
|
|
|
|
fn from_arg_ty(arg: Ty<'_>) -> Self {
|
2020-08-02 22:49:11 +00:00
|
|
|
match arg.kind() {
|
2019-07-06 01:18:38 +00:00
|
|
|
ty::Ref(_, _, _) => PeekCallKind::ByRef,
|
|
|
|
_ => PeekCallKind::ByVal,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub struct PeekCall {
|
|
|
|
arg: Local,
|
|
|
|
kind: PeekCallKind,
|
|
|
|
span: Span,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PeekCall {
|
|
|
|
fn from_terminator<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
terminator: &mir::Terminator<'tcx>,
|
|
|
|
) -> Option<Self> {
|
2019-10-20 20:09:36 +00:00
|
|
|
use mir::Operand;
|
2019-07-06 01:18:38 +00:00
|
|
|
|
|
|
|
let span = terminator.source_info.span;
|
|
|
|
if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
|
|
|
|
&terminator.kind
|
|
|
|
{
|
2021-03-08 16:18:03 +00:00
|
|
|
if let ty::FnDef(def_id, substs) = *func.literal.ty().kind() {
|
2019-07-06 01:18:38 +00:00
|
|
|
let name = tcx.item_name(def_id);
|
2022-05-13 13:50:21 +00:00
|
|
|
if !tcx.is_intrinsic(def_id) || name != sym::rustc_peek {
|
2019-07-06 01:18:38 +00:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(args.len(), 1);
|
|
|
|
let kind = PeekCallKind::from_arg_ty(substs.type_at(0));
|
2019-10-20 20:09:36 +00:00
|
|
|
let arg = match &args[0] {
|
|
|
|
Operand::Copy(place) | Operand::Move(place) => {
|
|
|
|
if let Some(local) = place.as_local() {
|
|
|
|
local
|
|
|
|
} else {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(PeekMustBeNotTemporary { span });
|
2019-10-20 20:09:36 +00:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
2019-07-06 01:18:38 +00:00
|
|
|
_ => {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(PeekMustBeNotTemporary { span });
|
2019-07-06 01:18:38 +00:00
|
|
|
return None;
|
2016-05-20 11:18:03 +00:00
|
|
|
}
|
2019-07-06 01:18:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return Some(PeekCall { arg, kind, span });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-20 23:16:24 +00:00
|
|
|
pub trait RustcPeekAt<'tcx>: Analysis<'tcx> {
|
2019-07-06 01:18:38 +00:00
|
|
|
fn peek_at(
|
|
|
|
&self,
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2020-03-31 17:26:15 +00:00
|
|
|
place: mir::Place<'tcx>,
|
2020-08-28 20:26:25 +00:00
|
|
|
flow_state: &Self::Domain,
|
2019-07-06 01:18:38 +00:00
|
|
|
call: PeekCall,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-08-28 20:26:25 +00:00
|
|
|
impl<'tcx, A, D> RustcPeekAt<'tcx> for A
|
2019-07-06 01:18:38 +00:00
|
|
|
where
|
2020-08-28 20:26:25 +00:00
|
|
|
A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>,
|
2022-02-09 13:47:48 +00:00
|
|
|
D: JoinSemiLattice + Clone + BitSetExt<MovePathIndex>,
|
2019-07-06 01:18:38 +00:00
|
|
|
{
|
|
|
|
fn peek_at(
|
|
|
|
&self,
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2020-03-31 17:26:15 +00:00
|
|
|
place: mir::Place<'tcx>,
|
2020-08-28 20:26:25 +00:00
|
|
|
flow_state: &Self::Domain,
|
2019-07-06 01:18:38 +00:00
|
|
|
call: PeekCall,
|
|
|
|
) {
|
|
|
|
match self.move_data().rev_lookup.find(place.as_ref()) {
|
|
|
|
LookupResult::Exact(peek_mpi) => {
|
2022-02-09 13:47:48 +00:00
|
|
|
let bit_state = flow_state.contains(peek_mpi);
|
2019-07-06 01:18:38 +00:00
|
|
|
debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
|
|
|
|
if !bit_state {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(PeekBitNotSet { span: call.span });
|
2016-05-20 11:18:03 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-02 03:14:01 +00:00
|
|
|
|
2019-07-06 01:18:38 +00:00
|
|
|
LookupResult::Parent(..) => {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(PeekArgumentUntracked { span: call.span });
|
2019-07-06 01:18:38 +00:00
|
|
|
}
|
2016-05-20 11:18:03 +00:00
|
|
|
}
|
|
|
|
}
|
2016-05-11 20:03:57 +00:00
|
|
|
}
|
2019-10-02 03:14:01 +00:00
|
|
|
|
2020-04-10 18:00:11 +00:00
|
|
|
impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
|
|
|
|
fn peek_at(
|
|
|
|
&self,
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
place: mir::Place<'tcx>,
|
2022-06-05 23:22:54 +00:00
|
|
|
flow_state: &ChunkedBitSet<Local>,
|
2020-04-10 18:00:11 +00:00
|
|
|
call: PeekCall,
|
|
|
|
) {
|
2021-10-07 17:46:47 +00:00
|
|
|
info!(?place, "peek_at");
|
2021-10-16 01:45:14 +00:00
|
|
|
let Some(local) = place.as_local() else {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(PeekArgumentNotALocal { span: call.span });
|
2020-04-10 18:00:11 +00:00
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
if !flow_state.contains(local) {
|
2022-08-19 09:21:14 +00:00
|
|
|
tcx.sess.emit_err(PeekBitNotSet { span: call.span });
|
2020-04-10 18:00:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|