Auto merge of #148324 - Zalathar:rollup-yp35ktq, r=Zalathar

Rollup of 3 pull requests

Successful merges:

 - rust-lang/rust#148165 (Use `mut` less in dataflow analysis)
 - rust-lang/rust#148287 (Fix deferred cast checks using the wrong body for determining constness)
 - rust-lang/rust#148317 (bootstrap: Extract parts of `bootstrap::core::builder` into a `cli_paths` module)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors
2025-10-31 11:43:22 +00:00
25 changed files with 491 additions and 492 deletions
+9 -9
View File
@@ -44,7 +44,7 @@ fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Dom
}
fn apply_early_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
@@ -55,7 +55,7 @@ fn apply_early_statement_effect(
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
@@ -66,7 +66,7 @@ fn apply_primary_statement_effect(
}
fn apply_early_terminator_effect(
&mut self,
&self,
state: &mut Self::Domain,
term: &mir::Terminator<'tcx>,
loc: Location,
@@ -77,7 +77,7 @@ fn apply_early_terminator_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
term: &'mir mir::Terminator<'tcx>,
loc: Location,
@@ -92,7 +92,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
_state: &mut Self::Domain,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@@ -533,7 +533,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
}
fn apply_early_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -542,7 +542,7 @@ fn apply_early_statement_effect(
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
location: Location,
@@ -590,7 +590,7 @@ fn apply_primary_statement_effect(
}
fn apply_early_terminator_effect(
&mut self,
&self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -599,7 +599,7 @@ fn apply_early_terminator_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
+12 -14
View File
@@ -49,7 +49,7 @@
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results};
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use smallvec::SmallVec;
@@ -537,13 +537,11 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
mbcx.report_region_errors(nll_errors);
}
let (mut flow_analysis, flow_entry_states) =
get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
let flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
visit_results(
body,
traversal::reverse_postorder(body).map(|(bb, _)| bb),
&mut flow_analysis,
&flow_entry_states,
&flow_results,
&mut mbcx,
);
@@ -604,7 +602,7 @@ fn get_flow_results<'a, 'tcx>(
move_data: &'a MoveData<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
) -> (Borrowck<'a, 'tcx>, Results<BorrowckDomain>) {
) -> Results<'tcx, Borrowck<'a, 'tcx>> {
// We compute these three analyses individually, but them combine them into
// a single results so that `mbcx` can visit them all together.
let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint(
@@ -629,14 +627,14 @@ fn get_flow_results<'a, 'tcx>(
ever_inits: ever_inits.analysis,
};
assert_eq!(borrows.results.len(), uninits.results.len());
assert_eq!(borrows.results.len(), ever_inits.results.len());
let results: Results<_> =
itertools::izip!(borrows.results, uninits.results, ever_inits.results)
assert_eq!(borrows.entry_states.len(), uninits.entry_states.len());
assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len());
let entry_states: EntryStates<_> =
itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states)
.map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
.collect();
(analysis, results)
Results { analysis, entry_states }
}
pub(crate) struct BorrowckInferCtxt<'tcx> {
@@ -790,7 +788,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> {
fn visit_after_early_statement_effect(
&mut self,
_analysis: &mut Borrowck<'a, 'tcx>,
_analysis: &Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
stmt: &Statement<'tcx>,
location: Location,
@@ -865,7 +863,7 @@ fn visit_after_early_statement_effect(
fn visit_after_early_terminator_effect(
&mut self,
_analysis: &mut Borrowck<'a, 'tcx>,
_analysis: &Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
term: &Terminator<'tcx>,
loc: Location,
@@ -985,7 +983,7 @@ fn visit_after_early_terminator_effect(
fn visit_after_primary_terminator_effect(
&mut self,
_analysis: &mut Borrowck<'a, 'tcx>,
_analysis: &Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
term: &Terminator<'tcx>,
loc: Location,
@@ -332,7 +332,7 @@ fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Doma
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -341,7 +341,7 @@ fn apply_primary_statement_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
@@ -351,7 +351,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
state: &mut Self::Domain,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
+4 -2
View File
@@ -32,7 +32,7 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, ExprKind};
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_macros::{TypeFoldable, TypeVisitable};
@@ -63,6 +63,7 @@ pub(crate) struct CastCheck<'tcx> {
cast_ty: Ty<'tcx>,
cast_span: Span,
span: Span,
pub body_id: LocalDefId,
}
/// The kind of pointer and associated metadata (thin, length or vtable) - we
@@ -244,7 +245,8 @@ pub(crate) fn new(
span: Span,
) -> Result<CastCheck<'tcx>, ErrorGuaranteed> {
let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span);
let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span };
let check =
CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, body_id: fcx.body_id };
// For better error messages, check for some obviously unsized
// cases now. We do a more thorough check at the end, once
@@ -1,5 +1,5 @@
use std::ops::Deref;
use std::{fmt, iter, mem};
use std::{fmt, iter};
use itertools::Itertools;
use rustc_data_structures::fx::FxIndexSet;
@@ -72,16 +72,13 @@ pub(crate) enum DivergingBlockBehavior {
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn check_casts(&mut self) {
// don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors
// when writing to `self.param_env`.
let mut deferred_cast_checks = mem::take(&mut *self.deferred_cast_checks.borrow_mut());
let mut deferred_cast_checks = self.root_ctxt.deferred_cast_checks.borrow_mut();
debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len());
for cast in deferred_cast_checks.drain(..) {
let body_id = std::mem::replace(&mut self.body_id, cast.body_id);
cast.check(self);
self.body_id = body_id;
}
*self.deferred_cast_checks.borrow_mut() = deferred_cast_checks;
}
pub(in super::super) fn check_asms(&self) {
@@ -1,8 +1,7 @@
//! Random access inspection of the results of a dataflow analysis.
use std::borrow::Cow;
use std::cmp::Ordering;
use std::ops::{Deref, DerefMut};
use std::ops::Deref;
#[cfg(debug_assertions)]
use rustc_index::bit_set::DenseBitSet;
@@ -10,30 +9,20 @@
use super::{Analysis, Direction, Effect, EffectIndex, Results};
/// Some `ResultsCursor`s want to own an `Analysis`, and some want to borrow an `Analysis`, either
/// mutable or immutably. This type allows all of the above. It's similar to `Cow`, but `Cow`
/// doesn't allow mutable borrowing.
enum CowMut<'a, T> {
BorrowedMut(&'a mut T),
/// This is like `Cow`, but it lacks the `T: ToOwned` bound and doesn't support
/// `to_owned`/`into_owned`.
enum SimpleCow<'a, T> {
Borrowed(&'a T),
Owned(T),
}
impl<T> Deref for CowMut<'_, T> {
impl<T> Deref for SimpleCow<'_, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
CowMut::BorrowedMut(borrowed) => borrowed,
CowMut::Owned(owned) => owned,
}
}
}
impl<T> DerefMut for CowMut<'_, T> {
fn deref_mut(&mut self) -> &mut T {
match self {
CowMut::BorrowedMut(borrowed) => borrowed,
CowMut::Owned(owned) => owned,
SimpleCow::Borrowed(borrowed) => borrowed,
SimpleCow::Owned(owned) => owned,
}
}
}
@@ -53,8 +42,7 @@ pub struct ResultsCursor<'mir, 'tcx, A>
A: Analysis<'tcx>,
{
body: &'mir mir::Body<'tcx>,
analysis: CowMut<'mir, A>,
results: Cow<'mir, Results<A::Domain>>,
results: SimpleCow<'mir, Results<'tcx, A>>,
state: A::Domain,
pos: CursorPosition,
@@ -82,15 +70,10 @@ pub fn body(&self) -> &'mir mir::Body<'tcx> {
self.body
}
fn new(
body: &'mir mir::Body<'tcx>,
analysis: CowMut<'mir, A>,
results: Cow<'mir, Results<A::Domain>>,
) -> Self {
let bottom_value = analysis.bottom_value(body);
fn new(body: &'mir mir::Body<'tcx>, results: SimpleCow<'mir, Results<'tcx, A>>) -> Self {
let bottom_value = results.analysis.bottom_value(body);
ResultsCursor {
body,
analysis,
results,
// Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
@@ -106,21 +89,13 @@ fn new(
}
/// Returns a new cursor that takes ownership of and inspects analysis results.
pub fn new_owning(
body: &'mir mir::Body<'tcx>,
analysis: A,
results: Results<A::Domain>,
) -> Self {
Self::new(body, CowMut::Owned(analysis), Cow::Owned(results))
pub fn new_owning(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self {
Self::new(body, SimpleCow::Owned(results))
}
/// Returns a new cursor that borrows and inspects analysis results.
pub fn new_borrowing(
body: &'mir mir::Body<'tcx>,
analysis: &'mir mut A,
results: &'mir Results<A::Domain>,
) -> Self {
Self::new(body, CowMut::BorrowedMut(analysis), Cow::Borrowed(results))
pub fn new_borrowing(body: &'mir mir::Body<'tcx>, results: &'mir Results<'tcx, A>) -> Self {
Self::new(body, SimpleCow::Borrowed(results))
}
/// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled.
@@ -132,7 +107,7 @@ pub(crate) fn allow_unreachable(&mut self) {
/// Returns the `Analysis` used to generate the underlying `Results`.
pub fn analysis(&self) -> &A {
&self.analysis
&self.results.analysis
}
/// Resets the cursor to hold the entry set for the given basic block.
@@ -144,7 +119,7 @@ pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) {
#[cfg(debug_assertions)]
assert!(self.reachable_blocks.contains(block));
self.state.clone_from(&self.results[block]);
self.state.clone_from(&self.results.entry_states[block]);
self.pos = CursorPosition::block_entry(block);
self.state_needs_reset = false;
}
@@ -236,7 +211,7 @@ fn seek_after(&mut self, target: Location, effect: Effect) {
let target_effect_index = effect.at_index(target.statement_index);
A::Direction::apply_effects_in_range(
&mut *self.analysis,
&self.results.analysis,
&mut self.state,
target.block,
block_data,
@@ -251,8 +226,8 @@ fn seek_after(&mut self, target: Location, effect: Effect) {
///
/// This can be used, e.g., to apply the call return effect directly to the cursor without
/// creating an extra copy of the dataflow state.
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
f(&mut self.analysis, &mut self.state);
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
f(&self.results.analysis, &mut self.state);
self.state_needs_reset = true;
}
}
@@ -14,7 +14,7 @@ pub trait Direction {
/// Called by `iterate_to_fixpoint` during initial analysis computation.
fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
analysis: &A,
body: &mir::Body<'tcx>,
state: &mut A::Domain,
block: BasicBlock,
@@ -28,7 +28,7 @@ fn apply_effects_in_block<'mir, 'tcx, A>(
///
/// `effects.start()` must precede or equal `effects.end()` in this direction.
fn apply_effects_in_range<'tcx, A>(
analysis: &mut A,
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -40,10 +40,10 @@ fn apply_effects_in_range<'tcx, A>(
/// all locations in a basic block (starting from `entry_state` and to
/// visit them with `vis`.
fn visit_results_in_block<'mir, 'tcx, A>(
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
analysis: &mut A,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>;
@@ -56,7 +56,7 @@ impl Direction for Backward {
const IS_FORWARD: bool = false;
fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
analysis: &A,
body: &mir::Body<'tcx>,
state: &mut A::Domain,
block: BasicBlock,
@@ -129,7 +129,7 @@ fn apply_effects_in_block<'mir, 'tcx, A>(
}
fn apply_effects_in_range<'tcx, A>(
analysis: &mut A,
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -206,10 +206,10 @@ fn apply_effects_in_range<'tcx, A>(
}
fn visit_results_in_block<'mir, 'tcx, A>(
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
analysis: &mut A,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
@@ -242,7 +242,7 @@ impl Direction for Forward {
const IS_FORWARD: bool = true;
fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
analysis: &A,
body: &mir::Body<'tcx>,
state: &mut A::Domain,
block: BasicBlock,
@@ -312,7 +312,7 @@ fn apply_effects_in_block<'mir, 'tcx, A>(
}
fn apply_effects_in_range<'tcx, A>(
analysis: &mut A,
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -386,10 +386,10 @@ fn apply_effects_in_range<'tcx, A>(
}
fn visit_results_in_block<'mir, 'tcx, A>(
analysis: &A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
analysis: &mut A,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
@@ -1,7 +1,6 @@
//! A helpful diagram for debugging dataflow problems.
use std::borrow::Cow;
use std::cell::RefCell;
use std::ffi::OsString;
use std::path::PathBuf;
use std::sync::OnceLock;
@@ -33,8 +32,7 @@
pub(super) fn write_graphviz_results<'tcx, A>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
analysis: &mut A,
results: &Results<A::Domain>,
results: &Results<'tcx, A>,
pass_name: Option<&'static str>,
) -> std::io::Result<()>
where
@@ -81,7 +79,7 @@ pub(super) fn write_graphviz_results<'tcx, A>(
let mut buf = Vec::new();
let graphviz = Formatter::new(body, analysis, results, style);
let graphviz = Formatter::new(body, results, style);
let mut render_opts =
vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
@@ -206,12 +204,7 @@ struct Formatter<'mir, 'tcx, A>
A: Analysis<'tcx>,
{
body: &'mir Body<'tcx>,
// The `RefCell` is used because `<Formatter as Labeller>::node_label`
// takes `&self`, but it needs to modify the analysis. This is also the
// reason for the `Formatter`/`BlockFormatter` split; `BlockFormatter` has
// the operations that involve the mutation, i.e. within the `borrow_mut`.
analysis: RefCell<&'mir mut A>,
results: &'mir Results<A::Domain>,
results: &'mir Results<'tcx, A>,
style: OutputStyle,
reachable: DenseBitSet<BasicBlock>,
}
@@ -220,14 +213,9 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
fn new(
body: &'mir Body<'tcx>,
analysis: &'mir mut A,
results: &'mir Results<A::Domain>,
style: OutputStyle,
) -> Self {
fn new(body: &'mir Body<'tcx>, results: &'mir Results<'tcx, A>, style: OutputStyle) -> Self {
let reachable = traversal::reachable_as_bitset(body);
Formatter { body, analysis: analysis.into(), results, style, reachable }
Formatter { body, results, style, reachable }
}
}
@@ -265,12 +253,10 @@ fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
}
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
let analysis = &mut **self.analysis.borrow_mut();
let diffs = StateDiffCollector::run(self.body, *block, analysis, self.results, self.style);
let diffs = StateDiffCollector::run(self.body, *block, self.results, self.style);
let mut fmt = BlockFormatter {
cursor: ResultsCursor::new_borrowing(self.body, analysis, self.results),
cursor: ResultsCursor::new_borrowing(self.body, self.results),
style: self.style,
bg: Background::Light,
};
@@ -698,8 +684,7 @@ impl<D> StateDiffCollector<D> {
fn run<'tcx, A>(
body: &Body<'tcx>,
block: BasicBlock,
analysis: &mut A,
results: &Results<A::Domain>,
results: &Results<'tcx, A>,
style: OutputStyle,
) -> Self
where
@@ -707,12 +692,12 @@ fn run<'tcx, A>(
D: DebugWithContext<A>,
{
let mut collector = StateDiffCollector {
prev_state: analysis.bottom_value(body),
prev_state: results.analysis.bottom_value(body),
after: vec![],
before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]),
};
visit_results(body, std::iter::once(block), analysis, results, &mut collector);
visit_results(body, std::iter::once(block), results, &mut collector);
collector
}
}
@@ -736,7 +721,7 @@ fn visit_block_end(&mut self, state: &A::Domain) {
fn visit_after_early_statement_effect(
&mut self,
analysis: &mut A,
analysis: &A,
state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -749,7 +734,7 @@ fn visit_after_early_statement_effect(
fn visit_after_primary_statement_effect(
&mut self,
analysis: &mut A,
analysis: &A,
state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -760,7 +745,7 @@ fn visit_after_primary_statement_effect(
fn visit_after_early_terminator_effect(
&mut self,
analysis: &mut A,
analysis: &A,
state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -773,7 +758,7 @@ fn visit_after_early_terminator_effect(
fn visit_after_primary_terminator_effect(
&mut self,
analysis: &mut A,
analysis: &A,
state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -58,8 +58,7 @@
pub use self::cursor::ResultsCursor;
pub use self::direction::{Backward, Direction, Forward};
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
pub(crate) use self::results::AnalysisAndResults;
pub use self::results::Results;
pub use self::results::{EntryStates, Results};
pub use self::visitor::{ResultsVisitor, visit_reachable_results, visit_results};
/// Analysis domains are all bitsets of various kinds. This trait holds
@@ -136,7 +135,7 @@ pub trait Analysis<'tcx> {
/// analyses should not implement this without also implementing
/// `apply_primary_statement_effect`.
fn apply_early_statement_effect(
&mut self,
&self,
_state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -145,7 +144,7 @@ fn apply_early_statement_effect(
/// Updates the current dataflow state with the effect of evaluating a statement.
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -159,7 +158,7 @@ fn apply_primary_statement_effect(
/// analyses should not implement this without also implementing
/// `apply_primary_terminator_effect`.
fn apply_early_terminator_effect(
&mut self,
&self,
_state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -173,7 +172,7 @@ fn apply_early_terminator_effect(
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
/// initialized here.
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
_state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
@@ -189,7 +188,7 @@ fn apply_primary_terminator_effect<'mir>(
/// This is separate from `apply_primary_terminator_effect` to properly track state across
/// unwind edges.
fn apply_call_return_effect(
&mut self,
&self,
_state: &mut Self::Domain,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@@ -211,7 +210,7 @@ fn apply_call_return_effect(
/// engine doesn't need to clone the exit state for a block unless
/// `get_switch_int_data` is actually called.
fn get_switch_int_data(
&mut self,
&self,
_block: mir::BasicBlock,
_discr: &mir::Operand<'tcx>,
) -> Option<Self::SwitchIntData> {
@@ -220,7 +219,7 @@ fn get_switch_int_data(
/// See comments on `get_switch_int_data`.
fn apply_switch_int_edge_effect(
&mut self,
&self,
_data: &mut Self::SwitchIntData,
_state: &mut Self::Domain,
_value: SwitchTargetValue,
@@ -245,19 +244,21 @@ fn apply_switch_int_edge_effect(
/// Without a `pass_name` to differentiates them, only the results for the latest run will be
/// saved.
fn iterate_to_fixpoint<'mir>(
mut self,
self,
tcx: TyCtxt<'tcx>,
body: &'mir mir::Body<'tcx>,
pass_name: Option<&'static str>,
) -> AnalysisAndResults<'tcx, Self>
) -> Results<'tcx, Self>
where
Self: Sized,
Self::Domain: DebugWithContext<Self>,
{
let mut results = IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
self.initialize_start_block(body, &mut results[mir::START_BLOCK]);
let mut entry_states =
IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
self.initialize_start_block(body, &mut entry_states[mir::START_BLOCK]);
if Self::Direction::IS_BACKWARD && results[mir::START_BLOCK] != self.bottom_value(body) {
if Self::Direction::IS_BACKWARD && entry_states[mir::START_BLOCK] != self.bottom_value(body)
{
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
}
@@ -281,17 +282,17 @@ fn iterate_to_fixpoint<'mir>(
let mut state = self.bottom_value(body);
while let Some(bb) = dirty_queue.pop() {
// Set the state to the entry state of the block. This is equivalent to `state =
// results[bb].clone()`, but it saves an allocation, thus improving compile times.
state.clone_from(&results[bb]);
// entry_states[bb].clone()`, but it saves an allocation, thus improving compile times.
state.clone_from(&entry_states[bb]);
Self::Direction::apply_effects_in_block(
&mut self,
&self,
body,
&mut state,
bb,
&body[bb],
|target: BasicBlock, state: &Self::Domain| {
let set_changed = results[target].join(state);
let set_changed = entry_states[target].join(state);
if set_changed {
dirty_queue.insert(target);
}
@@ -299,14 +300,16 @@ fn iterate_to_fixpoint<'mir>(
);
}
let results = Results { analysis: self, entry_states };
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
let res = write_graphviz_results(tcx, body, &mut self, &results, pass_name);
let res = write_graphviz_results(tcx, body, &results, pass_name);
if let Err(e) = res {
error!("Failed to write graphviz dataflow results: {}", e);
}
}
AnalysisAndResults { analysis: self, results }
results
}
}
@@ -5,26 +5,26 @@
use super::{Analysis, ResultsCursor};
/// The results of a dataflow analysis that has converged to fixpoint. It only holds the domain
/// values at the entry of each basic block. Domain values in other parts of the block are
/// recomputed on the fly by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls).
pub type Results<D> = IndexVec<BasicBlock, D>;
pub type EntryStates<D> = IndexVec<BasicBlock, D>;
/// Utility type used in a few places where it's convenient to bundle an analysis with its results.
pub struct AnalysisAndResults<'tcx, A>
/// The results of a dataflow analysis that has converged to fixpoint. It holds the domain values
/// (states) at the entry of each basic block. Domain values in other parts of the block are
/// recomputed on the fly by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls). The
/// analysis is also present because it's often needed alongside the entry states.
pub struct Results<'tcx, A>
where
A: Analysis<'tcx>,
{
pub analysis: A,
pub results: Results<A::Domain>,
pub entry_states: EntryStates<A::Domain>,
}
impl<'tcx, A> AnalysisAndResults<'tcx, A>
impl<'tcx, A> Results<'tcx, A>
where
A: Analysis<'tcx>,
{
/// Creates a `ResultsCursor` that takes ownership of `self`.
pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
ResultsCursor::new_owning(body, self.analysis, self.results)
ResultsCursor::new_owning(body, self)
}
}
@@ -71,15 +71,15 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
///
/// | Location | Before | After |
/// |------------------------|-------------------|--------|
/// | (on_entry) | {102} ||
/// | (on_entry) | {102} | |
/// | statement 0 | +0 | +1 |
/// | statement 1 | +2 | +3 |
/// | `Call` terminator | +4 | +5 |
/// | (on unwind) | {102,0,1,2,3,4,5} ||
/// | (on unwind) | {102,0,1,2,3,4,5} | |
///
/// The `102` in the block's entry set is derived from the basic block index and ensures that the
/// expected state is unique across all basic blocks. Remember, it is generated by
/// `mock_results`, not from actually running `MockAnalysis` to fixpoint.
/// `mock_entry_states`, not from actually running `MockAnalysis` to fixpoint.
struct MockAnalysis<'tcx, D> {
body: &'tcx mir::Body<'tcx>,
dir: PhantomData<D>,
@@ -96,7 +96,7 @@ fn mock_entry_set(&self, bb: BasicBlock) -> DenseBitSet<usize> {
ret
}
fn mock_results(&self) -> IndexVec<BasicBlock, DenseBitSet<usize>> {
fn mock_entry_states(&self) -> IndexVec<BasicBlock, DenseBitSet<usize>> {
let empty = self.bottom_value(self.body);
let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks);
@@ -175,7 +175,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
}
fn apply_early_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -185,7 +185,7 @@ fn apply_early_statement_effect(
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -195,7 +195,7 @@ fn apply_primary_statement_effect(
}
fn apply_early_terminator_effect(
&mut self,
&self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -205,7 +205,7 @@ fn apply_early_terminator_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
@@ -255,7 +255,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
let body = analysis.body;
let mut cursor =
AnalysisAndResults { results: analysis.mock_results(), analysis }.into_results_cursor(body);
Results { entry_states: analysis.mock_entry_states(), analysis }.into_results_cursor(body);
cursor.allow_unreachable();
@@ -7,13 +7,12 @@
pub fn visit_results<'mir, 'tcx, A>(
body: &'mir mir::Body<'tcx>,
blocks: impl IntoIterator<Item = BasicBlock>,
analysis: &mut A,
results: &Results<A::Domain>,
results: &Results<'tcx, A>,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
let mut state = analysis.bottom_value(body);
let mut state = results.analysis.bottom_value(body);
#[cfg(debug_assertions)]
let reachable_blocks = mir::traversal::reachable_as_bitset(body);
@@ -23,22 +22,21 @@ pub fn visit_results<'mir, 'tcx, A>(
assert!(reachable_blocks.contains(block));
let block_data = &body[block];
state.clone_from(&results[block]);
A::Direction::visit_results_in_block(&mut state, block, block_data, analysis, vis);
state.clone_from(&results.entry_states[block]);
A::Direction::visit_results_in_block(&results.analysis, &mut state, block, block_data, vis);
}
}
/// Like `visit_results`, but only for reachable blocks.
pub fn visit_reachable_results<'mir, 'tcx, A>(
body: &'mir mir::Body<'tcx>,
analysis: &mut A,
results: &Results<A::Domain>,
results: &Results<'tcx, A>,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
let blocks = traversal::reachable(body).map(|(bb, _)| bb);
visit_results(body, blocks, analysis, results, vis)
visit_results(body, blocks, results, vis)
}
/// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in
@@ -53,7 +51,7 @@ fn visit_block_start(&mut self, _state: &A::Domain) {}
/// Called after the "early" effect of the given statement is applied to `state`.
fn visit_after_early_statement_effect(
&mut self,
_analysis: &mut A,
_analysis: &A,
_state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -63,7 +61,7 @@ fn visit_after_early_statement_effect(
/// Called after the "primary" effect of the given statement is applied to `state`.
fn visit_after_primary_statement_effect(
&mut self,
_analysis: &mut A,
_analysis: &A,
_state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -73,7 +71,7 @@ fn visit_after_primary_statement_effect(
/// Called after the "early" effect of the given terminator is applied to `state`.
fn visit_after_early_terminator_effect(
&mut self,
_analysis: &mut A,
_analysis: &A,
_state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -85,7 +83,7 @@ fn visit_after_early_terminator_effect(
/// The `call_return_effect` (if one exists) will *not* be applied to `state`.
fn visit_after_primary_terminator_effect(
&mut self,
_analysis: &mut A,
_analysis: &A,
_state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -11,7 +11,6 @@
/// At present, this is used as a very limited form of alias analysis. For example,
/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
/// immovable coroutines.
#[derive(Clone)]
pub struct MaybeBorrowedLocals;
impl MaybeBorrowedLocals {
@@ -34,7 +33,7 @@ fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) {
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
statement: &Statement<'tcx>,
location: Location,
@@ -43,7 +42,7 @@ fn apply_primary_statement_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir Terminator<'tcx>,
location: Location,
@@ -376,7 +376,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain)
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -400,7 +400,7 @@ fn apply_primary_statement_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
@@ -429,7 +429,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
state: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -448,7 +448,7 @@ fn apply_call_return_effect(
}
fn get_switch_int_data(
&mut self,
&self,
block: mir::BasicBlock,
discr: &mir::Operand<'tcx>,
) -> Option<Self::SwitchIntData> {
@@ -460,7 +460,7 @@ fn get_switch_int_data(
}
fn apply_switch_int_edge_effect(
&mut self,
&self,
data: &mut Self::SwitchIntData,
state: &mut Self::Domain,
value: SwitchTargetValue,
@@ -513,7 +513,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain)
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -527,7 +527,7 @@ fn apply_primary_statement_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
@@ -545,7 +545,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
state: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -564,7 +564,7 @@ fn apply_call_return_effect(
}
fn get_switch_int_data(
&mut self,
&self,
block: mir::BasicBlock,
discr: &mir::Operand<'tcx>,
) -> Option<Self::SwitchIntData> {
@@ -580,7 +580,7 @@ fn get_switch_int_data(
}
fn apply_switch_int_edge_effect(
&mut self,
&self,
data: &mut Self::SwitchIntData,
state: &mut Self::Domain,
value: SwitchTargetValue,
@@ -627,7 +627,7 @@ fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domai
#[instrument(skip(self, state), level = "debug")]
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
location: Location,
@@ -652,7 +652,7 @@ fn apply_primary_statement_effect(
#[instrument(skip(self, state, terminator), level = "debug")]
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
@@ -674,7 +674,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
state: &mut Self::Domain,
block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@@ -41,7 +41,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -50,7 +50,7 @@ fn apply_primary_statement_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
@@ -60,7 +60,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
state: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -278,7 +278,7 @@ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -294,7 +294,7 @@ fn apply_primary_statement_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
@@ -304,7 +304,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
state: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::cell::RefCell;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
@@ -53,7 +54,7 @@ fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &Statement<'tcx>,
_: Location,
@@ -97,7 +98,7 @@ fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &Statement<'tcx>,
_: Location,
@@ -115,12 +116,12 @@ fn apply_primary_statement_effect(
/// Dataflow analysis that determines whether each local requires storage at a
/// given location; i.e. whether its storage can go away without being observed.
pub struct MaybeRequiresStorage<'mir, 'tcx> {
borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>,
borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
}
impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
pub fn new(borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>) -> Self {
MaybeRequiresStorage { borrowed_locals }
MaybeRequiresStorage { borrowed_locals: RefCell::new(borrowed_locals) }
}
}
@@ -143,7 +144,7 @@ fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
}
fn apply_early_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
stmt: &Statement<'tcx>,
loc: Location,
@@ -176,7 +177,7 @@ fn apply_early_statement_effect(
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
_: &Statement<'tcx>,
loc: Location,
@@ -187,7 +188,7 @@ fn apply_primary_statement_effect(
}
fn apply_early_terminator_effect(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &Terminator<'tcx>,
loc: Location,
@@ -242,7 +243,7 @@ fn apply_early_terminator_effect(
}
fn apply_primary_terminator_effect<'t>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'t Terminator<'tcx>,
loc: Location,
@@ -283,7 +284,7 @@ fn apply_primary_terminator_effect<'t>(
}
fn apply_call_return_effect(
&mut self,
&self,
state: &mut Self::Domain,
_block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -294,9 +295,10 @@ fn apply_call_return_effect(
impl<'tcx> MaybeRequiresStorage<'_, 'tcx> {
/// Kill locals that are fully moved and have not been borrowed.
fn check_for_move(&mut self, state: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
let body = self.borrowed_locals.body();
let mut visitor = MoveVisitor { state, borrowed_locals: &mut self.borrowed_locals };
fn check_for_move(&self, state: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
let mut borrowed_locals = self.borrowed_locals.borrow_mut();
let body = borrowed_locals.body();
let mut visitor = MoveVisitor { state, borrowed_locals: &mut borrowed_locals };
visitor.visit_location(body, loc);
}
}
+3 -2
View File
@@ -17,8 +17,9 @@
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
};
pub use self::framework::{
Analysis, Backward, Direction, Forward, GenKill, JoinSemiLattice, MaybeReachable, Results,
ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_reachable_results, visit_results,
Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable,
Results, ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_reachable_results,
visit_results,
};
use self::move_paths::MoveData;
+9 -25
View File
@@ -721,27 +721,13 @@ fn locals_live_across_suspend_points<'tcx>(
// Calculate the MIR locals that have been previously borrowed (even if they are still active).
let borrowed_locals = MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine"));
let mut borrowed_locals_analysis1 = borrowed_locals.analysis;
let mut borrowed_locals_analysis2 = borrowed_locals_analysis1.clone(); // trivial
let borrowed_locals_cursor1 = ResultsCursor::new_borrowing(
body,
&mut borrowed_locals_analysis1,
&borrowed_locals.results,
);
let mut borrowed_locals_cursor2 = ResultsCursor::new_borrowing(
body,
&mut borrowed_locals_analysis2,
&borrowed_locals.results,
);
let borrowed_locals_cursor1 = ResultsCursor::new_borrowing(body, &borrowed_locals);
let mut borrowed_locals_cursor2 = ResultsCursor::new_borrowing(body, &borrowed_locals);
// Calculate the MIR locals that we need to keep storage around for.
let mut requires_storage =
let requires_storage =
MaybeRequiresStorage::new(borrowed_locals_cursor1).iterate_to_fixpoint(tcx, body, None);
let mut requires_storage_cursor = ResultsCursor::new_borrowing(
body,
&mut requires_storage.analysis,
&requires_storage.results,
);
let mut requires_storage_cursor = ResultsCursor::new_borrowing(body, &requires_storage);
// Calculate the liveness of MIR locals ignoring borrows.
let mut liveness =
@@ -813,8 +799,7 @@ fn locals_live_across_suspend_points<'tcx>(
body,
&saved_locals,
always_live_locals.clone(),
&mut requires_storage.analysis,
&requires_storage.results,
&requires_storage,
);
LivenessInfo {
@@ -879,8 +864,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
body: &'mir Body<'tcx>,
saved_locals: &'mir CoroutineSavedLocals,
always_live_locals: DenseBitSet<Local>,
analysis: &mut MaybeRequiresStorage<'mir, 'tcx>,
results: &Results<DenseBitSet<Local>>,
results: &Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
) -> BitMatrix<CoroutineSavedLocal, CoroutineSavedLocal> {
assert_eq!(body.local_decls.len(), saved_locals.domain_size());
@@ -900,7 +884,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
eligible_storage_live: DenseBitSet::new_empty(body.local_decls.len()),
};
visit_reachable_results(body, analysis, results, &mut visitor);
visit_reachable_results(body, results, &mut visitor);
let local_conflicts = visitor.local_conflicts;
@@ -943,7 +927,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>>
{
fn visit_after_early_statement_effect(
&mut self,
_analysis: &mut MaybeRequiresStorage<'a, 'tcx>,
_analysis: &MaybeRequiresStorage<'a, 'tcx>,
state: &DenseBitSet<Local>,
_statement: &Statement<'tcx>,
loc: Location,
@@ -953,7 +937,7 @@ fn visit_after_early_statement_effect(
fn visit_after_early_terminator_effect(
&mut self,
_analysis: &mut MaybeRequiresStorage<'a, 'tcx>,
_analysis: &MaybeRequiresStorage<'a, 'tcx>,
state: &DenseBitSet<Local>,
_terminator: &Terminator<'tcx>,
loc: Location,
@@ -3,6 +3,7 @@
//! Currently, this pass only propagates scalar values.
use std::assert_matches::assert_matches;
use std::cell::RefCell;
use std::fmt::Formatter;
use rustc_abi::{BackendRepr, FIRST_VARIANT, FieldIdx, Size, VariantIdx};
@@ -60,14 +61,12 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let map = Map::new(tcx, body, place_limit);
// Perform the actual dataflow analysis.
let mut const_ = debug_span!("analyze")
let const_ = debug_span!("analyze")
.in_scope(|| ConstAnalysis::new(tcx, body, map).iterate_to_fixpoint(tcx, body, None));
// Collect results and patch the body afterwards.
let mut visitor = Collector::new(tcx, &body.local_decls);
debug_span!("collect").in_scope(|| {
visit_reachable_results(body, &mut const_.analysis, &const_.results, &mut visitor)
});
debug_span!("collect").in_scope(|| visit_reachable_results(body, &const_, &mut visitor));
let mut patch = visitor.patch;
debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body));
}
@@ -85,7 +84,7 @@ struct ConstAnalysis<'a, 'tcx> {
map: Map<'tcx>,
tcx: TyCtxt<'tcx>,
local_decls: &'a LocalDecls<'tcx>,
ecx: InterpCx<'tcx, DummyMachine>,
ecx: RefCell<InterpCx<'tcx, DummyMachine>>,
typing_env: ty::TypingEnv<'tcx>,
}
@@ -111,7 +110,7 @@ fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
}
fn apply_primary_statement_effect(
&mut self,
&self,
state: &mut Self::Domain,
statement: &Statement<'tcx>,
_location: Location,
@@ -122,7 +121,7 @@ fn apply_primary_statement_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
state: &mut Self::Domain,
terminator: &'mir Terminator<'tcx>,
_location: Location,
@@ -135,7 +134,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
state: &mut Self::Domain,
_block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -153,7 +152,7 @@ fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map<'tcx>) -> Self {
map,
tcx,
local_decls: &body.local_decls,
ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine),
ecx: RefCell::new(InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine)),
typing_env,
}
}
@@ -410,6 +409,7 @@ fn handle_rvalue(
match self.eval_operand(operand, state) {
FlatSet::Elem(op) => self
.ecx
.borrow()
.int_to_int_or_float(&op, layout)
.discard_err()
.map_or(FlatSet::Top, |result| self.wrap_immediate(*result)),
@@ -424,6 +424,7 @@ fn handle_rvalue(
match self.eval_operand(operand, state) {
FlatSet::Elem(op) => self
.ecx
.borrow()
.float_to_float_or_int(&op, layout)
.discard_err()
.map_or(FlatSet::Top, |result| self.wrap_immediate(*result)),
@@ -454,6 +455,7 @@ fn handle_rvalue(
match self.eval_operand(operand, state) {
FlatSet::Elem(value) => self
.ecx
.borrow()
.unary_op(*op, &value)
.discard_err()
.map_or(FlatSet::Top, |val| self.wrap_immediate(*val)),
@@ -468,6 +470,7 @@ fn handle_rvalue(
let val = match null_op {
NullOp::OffsetOf(fields) => self
.ecx
.borrow()
.tcx
.offset_of_subfield(self.typing_env, layout, fields.iter())
.bytes(),
@@ -556,8 +559,11 @@ fn assign_operand(
}
}
Operand::Constant(box constant) => {
if let Some(constant) =
self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err()
if let Some(constant) = self
.ecx
.borrow()
.eval_mir_constant(&constant.const_, constant.span, None)
.discard_err()
{
self.assign_constant(state, place, constant, &[]);
}
@@ -587,7 +593,9 @@ fn assign_constant(
return;
}
}
operand = if let Some(operand) = self.ecx.project(&operand, proj_elem).discard_err() {
operand = if let Some(operand) =
self.ecx.borrow().project(&operand, proj_elem).discard_err()
{
operand
} else {
return;
@@ -598,17 +606,22 @@ fn assign_constant(
place,
operand,
&mut |elem, op| match elem {
TrackElem::Field(idx) => self.ecx.project_field(op, idx).discard_err(),
TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(),
TrackElem::Field(idx) => self.ecx.borrow().project_field(op, idx).discard_err(),
TrackElem::Variant(idx) => {
self.ecx.borrow().project_downcast(op, idx).discard_err()
}
TrackElem::Discriminant => {
let variant = self.ecx.read_discriminant(op).discard_err()?;
let discr_value =
self.ecx.discriminant_for_variant(op.layout.ty, variant).discard_err()?;
let variant = self.ecx.borrow().read_discriminant(op).discard_err()?;
let discr_value = self
.ecx
.borrow()
.discriminant_for_variant(op.layout.ty, variant)
.discard_err()?;
Some(discr_value.into())
}
TrackElem::DerefLen => {
let op: OpTy<'_> = self.ecx.deref_pointer(op).discard_err()?.into();
let len_usize = op.len(&self.ecx).discard_err()?;
let op: OpTy<'_> = self.ecx.borrow().deref_pointer(op).discard_err()?.into();
let len_usize = op.len(&self.ecx.borrow()).discard_err()?;
let layout = self
.tcx
.layout_of(self.typing_env.as_query_input(self.tcx.types.usize))
@@ -617,7 +630,7 @@ fn assign_constant(
}
},
&mut |place, op| {
if let Some(imm) = self.ecx.read_immediate_raw(op).discard_err()
if let Some(imm) = self.ecx.borrow().read_immediate_raw(op).discard_err()
&& let Some(imm) = imm.right()
{
let elem = self.wrap_immediate(*imm);
@@ -641,7 +654,7 @@ fn binary_op(
(FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom),
// Both sides are known, do the actual computation.
(FlatSet::Elem(left), FlatSet::Elem(right)) => {
match self.ecx.binary_op(op, &left, &right).discard_err() {
match self.ecx.borrow().binary_op(op, &left, &right).discard_err() {
// Ideally this would return an Immediate, since it's sometimes
// a pair and sometimes not. But as a hack we always return a pair
// and just make the 2nd component `Bottom` when it does not exist.
@@ -714,8 +727,11 @@ fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Opt
return None;
}
let enum_ty_layout = self.tcx.layout_of(self.typing_env.as_query_input(enum_ty)).ok()?;
let discr_value =
self.ecx.discriminant_for_variant(enum_ty_layout.ty, variant_index).discard_err()?;
let discr_value = self
.ecx
.borrow()
.discriminant_for_variant(enum_ty_layout.ty, variant_index)
.discard_err()?;
Some(discr_value.to_scalar())
}
@@ -946,7 +962,7 @@ impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx>
#[instrument(level = "trace", skip(self, analysis, statement))]
fn visit_after_early_statement_effect(
&mut self,
analysis: &mut ConstAnalysis<'_, 'tcx>,
analysis: &ConstAnalysis<'_, 'tcx>,
state: &State<FlatSet<Scalar>>,
statement: &Statement<'tcx>,
location: Location,
@@ -956,7 +972,7 @@ fn visit_after_early_statement_effect(
OperandCollector {
state,
visitor: self,
ecx: &mut analysis.ecx,
ecx: &mut analysis.ecx.borrow_mut(),
map: &analysis.map,
}
.visit_rvalue(rvalue, location);
@@ -968,7 +984,7 @@ fn visit_after_early_statement_effect(
#[instrument(level = "trace", skip(self, analysis, statement))]
fn visit_after_primary_statement_effect(
&mut self,
analysis: &mut ConstAnalysis<'_, 'tcx>,
analysis: &ConstAnalysis<'_, 'tcx>,
state: &State<FlatSet<Scalar>>,
statement: &Statement<'tcx>,
location: Location,
@@ -978,9 +994,12 @@ fn visit_after_primary_statement_effect(
// Don't overwrite the assignment if it already uses a constant (to keep the span).
}
StatementKind::Assign(box (place, _)) => {
if let Some(value) =
self.try_make_constant(&mut analysis.ecx, place, state, &analysis.map)
{
if let Some(value) = self.try_make_constant(
&mut analysis.ecx.borrow_mut(),
place,
state,
&analysis.map,
) {
self.patch.assignments.insert(location, value);
}
}
@@ -990,13 +1009,18 @@ fn visit_after_primary_statement_effect(
fn visit_after_early_terminator_effect(
&mut self,
analysis: &mut ConstAnalysis<'_, 'tcx>,
analysis: &ConstAnalysis<'_, 'tcx>,
state: &State<FlatSet<Scalar>>,
terminator: &Terminator<'tcx>,
location: Location,
) {
OperandCollector { state, visitor: self, ecx: &mut analysis.ecx, map: &analysis.map }
.visit_terminator(terminator, location);
OperandCollector {
state,
visitor: self,
ecx: &mut analysis.ecx.borrow_mut(),
map: &analysis.map,
}
.visit_terminator(terminator, location);
}
}
@@ -146,7 +146,7 @@
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_mir_dataflow::{Analysis, Results};
use rustc_mir_dataflow::{Analysis, EntryStates};
use tracing::{debug, trace};
pub(super) struct DestinationPropagation;
@@ -173,7 +173,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let points = DenseLocationMap::new(body);
let mut relevant = RelevantLocals::compute(&candidates, body.local_decls.len());
let mut live = save_as_intervals(&points, body, &relevant, live.results);
let mut live = save_as_intervals(&points, body, &relevant, live.entry_states);
dest_prop_mir_dump(tcx, body, &points, &live, &relevant);
@@ -506,7 +506,7 @@ fn save_as_intervals<'tcx>(
elements: &DenseLocationMap,
body: &Body<'tcx>,
relevant: &RelevantLocals,
results: Results<DenseBitSet<Local>>,
entry_states: EntryStates<DenseBitSet<Local>>,
) -> SparseIntervalMatrix<RelevantLocal, TwoStepIndex> {
let mut values = SparseIntervalMatrix::new(2 * elements.num_points());
let mut state = MaybeLiveLocals.bottom_value(body);
@@ -529,7 +529,7 @@ fn save_as_intervals<'tcx>(
continue;
}
state.clone_from(&results[block]);
state.clone_from(&entry_states[block]);
let block_data = &body.basic_blocks[block];
let loc = Location { block, statement_index: block_data.statements.len() };
+3 -3
View File
@@ -1160,7 +1160,7 @@ fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) {
}
fn apply_primary_statement_effect(
&mut self,
&self,
trans: &mut Self::Domain,
statement: &Statement<'tcx>,
location: Location,
@@ -1169,7 +1169,7 @@ fn apply_primary_statement_effect(
}
fn apply_primary_terminator_effect<'mir>(
&mut self,
&self,
trans: &mut Self::Domain,
terminator: &'mir Terminator<'tcx>,
location: Location,
@@ -1179,7 +1179,7 @@ fn apply_primary_terminator_effect<'mir>(
}
fn apply_call_return_effect(
&mut self,
&self,
_trans: &mut Self::Domain,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
+245
View File
@@ -0,0 +1,245 @@
//! Various pieces of code for dealing with "paths" passed to bootstrap on the
//! command-line, extracted from `core/builder/mod.rs` because that file is
//! large and hard to navigate.
use std::fmt::{self, Debug};
use std::path::PathBuf;
use crate::core::builder::{Builder, Kind, PathSet, ShouldRun, StepDescription};
pub(crate) const PATH_REMAP: &[(&str, &[&str])] = &[
// bootstrap.toml uses `rust-analyzer-proc-macro-srv`, but the
// actual path is `proc-macro-srv-cli`
("rust-analyzer-proc-macro-srv", &["src/tools/rust-analyzer/crates/proc-macro-srv-cli"]),
// Make `x test tests` function the same as `x t tests/*`
(
"tests",
&[
// tidy-alphabetical-start
"tests/assembly-llvm",
"tests/codegen-llvm",
"tests/codegen-units",
"tests/coverage",
"tests/coverage-run-rustdoc",
"tests/crashes",
"tests/debuginfo",
"tests/incremental",
"tests/mir-opt",
"tests/pretty",
"tests/run-make",
"tests/run-make-cargo",
"tests/rustdoc",
"tests/rustdoc-gui",
"tests/rustdoc-js",
"tests/rustdoc-js-std",
"tests/rustdoc-json",
"tests/rustdoc-ui",
"tests/ui",
"tests/ui-fulldeps",
// tidy-alphabetical-end
],
),
];
pub(crate) fn remap_paths(paths: &mut Vec<PathBuf>) {
let mut remove = vec![];
let mut add = vec![];
for (i, path) in paths.iter().enumerate().filter_map(|(i, path)| path.to_str().map(|s| (i, s)))
{
for &(search, replace) in PATH_REMAP {
// Remove leading and trailing slashes so `tests/` and `tests` are equivalent
if path.trim_matches(std::path::is_separator) == search {
remove.push(i);
add.extend(replace.iter().map(PathBuf::from));
break;
}
}
}
remove.sort();
remove.dedup();
for idx in remove.into_iter().rev() {
paths.remove(idx);
}
paths.append(&mut add);
}
#[derive(Clone, PartialEq)]
pub(crate) struct CLIStepPath {
pub(crate) path: PathBuf,
pub(crate) will_be_executed: bool,
}
#[cfg(test)]
impl CLIStepPath {
pub(crate) fn will_be_executed(mut self, will_be_executed: bool) -> Self {
self.will_be_executed = will_be_executed;
self
}
}
impl Debug for CLIStepPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.path.display())
}
}
impl From<PathBuf> for CLIStepPath {
fn from(path: PathBuf) -> Self {
Self { path, will_be_executed: false }
}
}
/// Combines a `StepDescription` with its corresponding `ShouldRun`.
struct StepExtra<'a> {
desc: &'a StepDescription,
should_run: ShouldRun<'a>,
}
struct StepToRun<'a> {
sort_index: usize,
desc: &'a StepDescription,
pathsets: Vec<PathSet>,
}
pub(crate) fn match_paths_to_steps_and_run(
builder: &Builder<'_>,
step_descs: &[StepDescription],
paths: &[PathBuf],
) {
// Obtain `ShouldRun` information for each step, so that we know which
// paths to match it against.
let steps = step_descs
.iter()
.map(|desc| StepExtra {
desc,
should_run: (desc.should_run)(ShouldRun::new(builder, desc.kind)),
})
.collect::<Vec<_>>();
// FIXME(Zalathar): This particular check isn't related to path-to-step
// matching, and should probably be hoisted to somewhere much earlier.
if builder.download_rustc() && (builder.kind == Kind::Dist || builder.kind == Kind::Install) {
eprintln!(
"ERROR: '{}' subcommand is incompatible with `rust.download-rustc`.",
builder.kind.as_str()
);
crate::exit!(1);
}
// sanity checks on rules
for StepExtra { desc, should_run } in &steps {
assert!(!should_run.paths.is_empty(), "{:?} should have at least one pathset", desc.name);
}
if paths.is_empty() || builder.config.include_default_paths {
for StepExtra { desc, should_run } in &steps {
if desc.default && should_run.is_really_default() {
desc.maybe_run(builder, should_run.paths.iter().cloned().collect());
}
}
}
// Attempt to resolve paths to be relative to the builder source directory.
let mut paths: Vec<PathBuf> = paths
.iter()
.map(|original_path| {
let mut path = original_path.clone();
// Someone could run `x <cmd> <path>` from a different repository than the source
// directory.
// In that case, we should not try to resolve the paths relative to the working
// directory, but rather relative to the source directory.
// So we forcefully "relocate" the path to the source directory here.
if !path.is_absolute() {
path = builder.src.join(path);
}
// If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy`
if !path.exists() {
// Use the original path here
return original_path.clone();
}
// Make the path absolute, strip the prefix, and convert to a PathBuf.
match std::path::absolute(&path) {
Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(),
Err(e) => {
eprintln!("ERROR: {e:?}");
panic!("Due to the above error, failed to resolve path: {path:?}");
}
}
})
.collect();
remap_paths(&mut paths);
// Handle all test suite paths.
// (This is separate from the loop below to avoid having to handle multiple paths in `is_suite_path` somehow.)
paths.retain(|path| {
for StepExtra { desc, should_run } in &steps {
if let Some(suite) = should_run.is_suite_path(path) {
desc.maybe_run(builder, vec![suite.clone()]);
return false;
}
}
true
});
if paths.is_empty() {
return;
}
let mut paths: Vec<CLIStepPath> = paths.into_iter().map(|p| p.into()).collect();
let mut path_lookup: Vec<(CLIStepPath, bool)> =
paths.clone().into_iter().map(|p| (p, false)).collect();
// Before actually running (non-suite) steps, collect them into a list of structs
// so that we can then sort the list to preserve CLI order as much as possible.
let mut steps_to_run = vec![];
for StepExtra { desc, should_run } in &steps {
let pathsets = should_run.pathset_for_paths_removing_matches(&mut paths, desc.kind);
// This value is used for sorting the step execution order.
// By default, `usize::MAX` is used as the index for steps to assign them the lowest priority.
//
// If we resolve the step's path from the given CLI input, this value will be updated with
// the step's actual index.
let mut closest_index = usize::MAX;
// Find the closest index from the original list of paths given by the CLI input.
for (index, (path, is_used)) in path_lookup.iter_mut().enumerate() {
if !*is_used && !paths.contains(path) {
closest_index = index;
*is_used = true;
break;
}
}
steps_to_run.push(StepToRun { sort_index: closest_index, desc, pathsets });
}
// Sort the steps before running them to respect the CLI order.
steps_to_run.sort_by_key(|step| step.sort_index);
// Handle all PathSets.
for StepToRun { sort_index: _, desc, pathsets } in steps_to_run {
if !pathsets.is_empty() {
desc.maybe_run(builder, pathsets);
}
}
paths.retain(|p| !p.will_be_executed);
if !paths.is_empty() {
eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths);
eprintln!(
"HELP: run `x.py {} --help --verbose` to show a list of available paths",
builder.kind.as_str()
);
eprintln!(
"NOTE: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`"
);
crate::exit!(1);
}
}
+4 -223
View File
@@ -1,7 +1,7 @@
use std::any::{Any, type_name};
use std::cell::{Cell, RefCell};
use std::collections::BTreeSet;
use std::fmt::{self, Debug, Write};
use std::fmt::{Debug, Write};
use std::hash::Hash;
use std::ops::Deref;
use std::path::{Path, PathBuf};
@@ -20,6 +20,7 @@
use crate::core::build_steps::{
check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor,
};
use crate::core::builder::cli_paths::CLIStepPath;
use crate::core::config::flags::Subcommand;
use crate::core::config::{DryRun, TargetSelection};
use crate::utils::build_stamp::BuildStamp;
@@ -29,7 +30,7 @@
use crate::{Build, Crate, trace};
mod cargo;
mod cli_paths;
#[cfg(test)]
mod tests;
@@ -424,88 +425,6 @@ pub fn assert_single_path(&self) -> &TaskPath {
}
}
const PATH_REMAP: &[(&str, &[&str])] = &[
// bootstrap.toml uses `rust-analyzer-proc-macro-srv`, but the
// actual path is `proc-macro-srv-cli`
("rust-analyzer-proc-macro-srv", &["src/tools/rust-analyzer/crates/proc-macro-srv-cli"]),
// Make `x test tests` function the same as `x t tests/*`
(
"tests",
&[
// tidy-alphabetical-start
"tests/assembly-llvm",
"tests/codegen-llvm",
"tests/codegen-units",
"tests/coverage",
"tests/coverage-run-rustdoc",
"tests/crashes",
"tests/debuginfo",
"tests/incremental",
"tests/mir-opt",
"tests/pretty",
"tests/run-make",
"tests/run-make-cargo",
"tests/rustdoc",
"tests/rustdoc-gui",
"tests/rustdoc-js",
"tests/rustdoc-js-std",
"tests/rustdoc-json",
"tests/rustdoc-ui",
"tests/ui",
"tests/ui-fulldeps",
// tidy-alphabetical-end
],
),
];
fn remap_paths(paths: &mut Vec<PathBuf>) {
let mut remove = vec![];
let mut add = vec![];
for (i, path) in paths.iter().enumerate().filter_map(|(i, path)| path.to_str().map(|s| (i, s)))
{
for &(search, replace) in PATH_REMAP {
// Remove leading and trailing slashes so `tests/` and `tests` are equivalent
if path.trim_matches(std::path::is_separator) == search {
remove.push(i);
add.extend(replace.iter().map(PathBuf::from));
break;
}
}
}
remove.sort();
remove.dedup();
for idx in remove.into_iter().rev() {
paths.remove(idx);
}
paths.append(&mut add);
}
#[derive(Clone, PartialEq)]
struct CLIStepPath {
path: PathBuf,
will_be_executed: bool,
}
#[cfg(test)]
impl CLIStepPath {
fn will_be_executed(mut self, will_be_executed: bool) -> Self {
self.will_be_executed = will_be_executed;
self
}
}
impl Debug for CLIStepPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.path.display())
}
}
impl From<PathBuf> for CLIStepPath {
fn from(path: PathBuf) -> Self {
Self { path, will_be_executed: false }
}
}
impl StepDescription {
fn from<S: Step>(kind: Kind) -> StepDescription {
StepDescription {
@@ -554,144 +473,6 @@ fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool {
}
false
}
fn run(v: &[StepDescription], builder: &Builder<'_>, paths: &[PathBuf]) {
let should_runs = v
.iter()
.map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind)))
.collect::<Vec<_>>();
if builder.download_rustc() && (builder.kind == Kind::Dist || builder.kind == Kind::Install)
{
eprintln!(
"ERROR: '{}' subcommand is incompatible with `rust.download-rustc`.",
builder.kind.as_str()
);
crate::exit!(1);
}
// sanity checks on rules
for (desc, should_run) in v.iter().zip(&should_runs) {
assert!(
!should_run.paths.is_empty(),
"{:?} should have at least one pathset",
desc.name
);
}
if paths.is_empty() || builder.config.include_default_paths {
for (desc, should_run) in v.iter().zip(&should_runs) {
if desc.default && should_run.is_really_default() {
desc.maybe_run(builder, should_run.paths.iter().cloned().collect());
}
}
}
// Attempt to resolve paths to be relative to the builder source directory.
let mut paths: Vec<PathBuf> = paths
.iter()
.map(|original_path| {
let mut path = original_path.clone();
// Someone could run `x <cmd> <path>` from a different repository than the source
// directory.
// In that case, we should not try to resolve the paths relative to the working
// directory, but rather relative to the source directory.
// So we forcefully "relocate" the path to the source directory here.
if !path.is_absolute() {
path = builder.src.join(path);
}
// If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy`
if !path.exists() {
// Use the original path here
return original_path.clone();
}
// Make the path absolute, strip the prefix, and convert to a PathBuf.
match std::path::absolute(&path) {
Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(),
Err(e) => {
eprintln!("ERROR: {e:?}");
panic!("Due to the above error, failed to resolve path: {path:?}");
}
}
})
.collect();
remap_paths(&mut paths);
// Handle all test suite paths.
// (This is separate from the loop below to avoid having to handle multiple paths in `is_suite_path` somehow.)
paths.retain(|path| {
for (desc, should_run) in v.iter().zip(&should_runs) {
if let Some(suite) = should_run.is_suite_path(path) {
desc.maybe_run(builder, vec![suite.clone()]);
return false;
}
}
true
});
if paths.is_empty() {
return;
}
let mut paths: Vec<CLIStepPath> = paths.into_iter().map(|p| p.into()).collect();
let mut path_lookup: Vec<(CLIStepPath, bool)> =
paths.clone().into_iter().map(|p| (p, false)).collect();
// List of `(usize, &StepDescription, Vec<PathSet>)` where `usize` is the closest index of a path
// compared to the given CLI paths. So we can respect to the CLI order by using this value to sort
// the steps.
let mut steps_to_run = vec![];
for (desc, should_run) in v.iter().zip(&should_runs) {
let pathsets = should_run.pathset_for_paths_removing_matches(&mut paths, desc.kind);
// This value is used for sorting the step execution order.
// By default, `usize::MAX` is used as the index for steps to assign them the lowest priority.
//
// If we resolve the step's path from the given CLI input, this value will be updated with
// the step's actual index.
let mut closest_index = usize::MAX;
// Find the closest index from the original list of paths given by the CLI input.
for (index, (path, is_used)) in path_lookup.iter_mut().enumerate() {
if !*is_used && !paths.contains(path) {
closest_index = index;
*is_used = true;
break;
}
}
steps_to_run.push((closest_index, desc, pathsets));
}
// Sort the steps before running them to respect the CLI order.
steps_to_run.sort_by_key(|(index, _, _)| *index);
// Handle all PathSets.
for (_index, desc, pathsets) in steps_to_run {
if !pathsets.is_empty() {
desc.maybe_run(builder, pathsets);
}
}
paths.retain(|p| !p.will_be_executed);
if !paths.is_empty() {
eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths);
eprintln!(
"HELP: run `x.py {} --help --verbose` to show a list of available paths",
builder.kind.as_str()
);
eprintln!(
"NOTE: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`"
);
crate::exit!(1);
}
}
}
enum ReallyDefault<'a> {
@@ -1349,7 +1130,7 @@ pub fn doc_rust_lang_org_channel(&self) -> String {
}
fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) {
StepDescription::run(v, self, paths);
cli_paths::match_paths_to_steps_and_run(self, v, paths);
}
/// Returns if `std` should be statically linked into `rustc_driver`.
+1
View File
@@ -7,6 +7,7 @@
use super::*;
use crate::Flags;
use crate::core::build_steps::doc::DocumentationFormat;
use crate::core::builder::cli_paths::PATH_REMAP;
use crate::core::config::Config;
use crate::utils::cache::ExecutedStep;
use crate::utils::helpers::get_host_target;
+5 -1
View File
@@ -1,5 +1,9 @@
//@ check-pass
//@ revisions: stock cnst
#![cfg_attr(cnst, feature(const_trait_impl))]
const _: fn(&String) = |s| { &*s as &str; };
const _: fn(&String) = |s| {
&*s as &str;
};
fn main() {}