diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index be02e2f48dfd8..d7ea8e1bcc2cc 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -116,7 +116,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( let var_origins = infcx.get_region_var_origins(); // If requested, emit legacy polonius facts. - polonius::emit_facts( + polonius::legacy::emit_facts( &mut all_facts, infcx.tcx, location_table, diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs similarity index 100% rename from compiler/rustc_borrowck/src/polonius/loan_invalidations.rs rename to compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs diff --git a/compiler/rustc_borrowck/src/polonius/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs similarity index 100% rename from compiler/rustc_borrowck/src/polonius/loan_kills.rs rename to compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs new file mode 100644 index 0000000000000..9fccc00bdaf0e --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -0,0 +1,184 @@ +//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation. +//! +//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature +//! parity. + +use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK}; +use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; +use tracing::debug; + +use crate::borrow_set::BorrowSet; +use crate::facts::{AllFacts, PoloniusRegionVid}; +use crate::location::LocationTable; +use crate::type_check::free_region_relations::UniversalRegionRelations; + +mod loan_invalidations; +mod loan_kills; + +/// When requested, emit most of the facts needed by polonius: +/// - moves and assignments +/// - universal regions and their relations +/// - CFG points and edges +/// - loan kills +/// - loan invalidations +/// +/// The rest of the facts are emitted during typeck and liveness. +pub(crate) fn emit_facts<'tcx>( + all_facts: &mut Option, + tcx: TyCtxt<'tcx>, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, + move_data: &MoveData<'_>, + universal_region_relations: &UniversalRegionRelations<'_>, +) { + let Some(all_facts) = all_facts else { + // We don't do anything if there are no facts to fill. + return; + }; + let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); + emit_move_facts(all_facts, move_data, location_table, body); + emit_universal_region_facts(all_facts, borrow_set, universal_region_relations); + emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set); + emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set); +} + +/// Emit facts needed for move/init analysis: moves and assignments. +fn emit_move_facts( + all_facts: &mut AllFacts, + move_data: &MoveData<'_>, + location_table: &LocationTable, + body: &Body<'_>, +) { + all_facts + .path_is_var + .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); + + for (child, move_path) in move_data.move_paths.iter_enumerated() { + if let Some(parent) = move_path.parent { + all_facts.child_path.push((child, parent)); + } + } + + let fn_entry_start = + location_table.start_index(Location { block: START_BLOCK, statement_index: 0 }); + + // initialized_at + for init in move_data.inits.iter() { + match init.location { + InitLocation::Statement(location) => { + let block_data = &body[location.block]; + let is_terminator = location.statement_index == block_data.statements.len(); + + if is_terminator && init.kind == InitKind::NonPanicPathOnly { + // We are at the terminator of an init that has a panic path, + // and where the init should not happen on panic + + for successor in block_data.terminator().successors() { + if body[successor].is_cleanup { + continue; + } + + // The initialization happened in (or rather, when arriving at) + // the successors, but not in the unwind block. + let first_statement = Location { block: successor, statement_index: 0 }; + all_facts + .path_assigned_at_base + .push((init.path, location_table.start_index(first_statement))); + } + } else { + // In all other cases, the initialization just happens at the + // midpoint, like any other effect. + all_facts + .path_assigned_at_base + .push((init.path, location_table.mid_index(location))); + } + } + // Arguments are initialized on function entry + InitLocation::Argument(local) => { + assert!(body.local_kind(local) == LocalKind::Arg); + all_facts.path_assigned_at_base.push((init.path, fn_entry_start)); + } + } + } + + for (local, path) in move_data.rev_lookup.iter_locals_enumerated() { + if body.local_kind(local) != LocalKind::Arg { + // Non-arguments start out deinitialised; we simulate this with an + // initial move: + all_facts.path_moved_at_base.push((path, fn_entry_start)); + } + } + + // moved_out_at + // deinitialisation is assumed to always happen! + all_facts + .path_moved_at_base + .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); +} + +/// Emit universal regions facts, and their relations. +fn emit_universal_region_facts( + all_facts: &mut AllFacts, + borrow_set: &BorrowSet<'_>, + universal_region_relations: &UniversalRegionRelations<'_>, +) { + // 1: universal regions are modeled in Polonius as a pair: + // - the universal region vid itself. + // - a "placeholder loan" associated to this universal region. Since they don't exist in + // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index + // added to the existing number of loans, as if they succeeded them in the set. + // + let universal_regions = &universal_region_relations.universal_regions; + all_facts + .universal_region + .extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from)); + let borrow_count = borrow_set.len(); + debug!( + "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}", + universal_regions.len(), + borrow_count + ); + + for universal_region in universal_regions.universal_regions_iter() { + let universal_region_idx = universal_region.index(); + let placeholder_loan_idx = borrow_count + universal_region_idx; + all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into())); + } + + // 2: the universal region relations `outlives` constraints are emitted as + // `known_placeholder_subset` facts. + for (fr1, fr2) in universal_region_relations.known_outlives() { + if fr1 != fr2 { + debug!( + "emit_universal_region_facts: emitting polonius `known_placeholder_subset` \ + fr1={:?}, fr2={:?}", + fr1, fr2 + ); + all_facts.known_placeholder_subset.push((fr1.into(), fr2.into())); + } + } +} + +/// Emit facts about loan invalidations. +fn emit_loan_invalidations_facts<'tcx>( + all_facts: &mut AllFacts, + tcx: TyCtxt<'tcx>, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, +) { + loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set); +} + +/// Emit facts about CFG points and edges, as well as locations where loans are killed. +fn emit_cfg_and_loan_kills_facts<'tcx>( + all_facts: &mut AllFacts, + tcx: TyCtxt<'tcx>, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, +) { + loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set); +} diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 9fccc00bdaf0e..9c1583f198862 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -1,184 +1 @@ -//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation. -//! -//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature -//! parity. - -use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK}; -use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; -use tracing::debug; - -use crate::borrow_set::BorrowSet; -use crate::facts::{AllFacts, PoloniusRegionVid}; -use crate::location::LocationTable; -use crate::type_check::free_region_relations::UniversalRegionRelations; - -mod loan_invalidations; -mod loan_kills; - -/// When requested, emit most of the facts needed by polonius: -/// - moves and assignments -/// - universal regions and their relations -/// - CFG points and edges -/// - loan kills -/// - loan invalidations -/// -/// The rest of the facts are emitted during typeck and liveness. -pub(crate) fn emit_facts<'tcx>( - all_facts: &mut Option, - tcx: TyCtxt<'tcx>, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, - move_data: &MoveData<'_>, - universal_region_relations: &UniversalRegionRelations<'_>, -) { - let Some(all_facts) = all_facts else { - // We don't do anything if there are no facts to fill. - return; - }; - let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); - emit_move_facts(all_facts, move_data, location_table, body); - emit_universal_region_facts(all_facts, borrow_set, universal_region_relations); - emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set); - emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set); -} - -/// Emit facts needed for move/init analysis: moves and assignments. -fn emit_move_facts( - all_facts: &mut AllFacts, - move_data: &MoveData<'_>, - location_table: &LocationTable, - body: &Body<'_>, -) { - all_facts - .path_is_var - .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); - - for (child, move_path) in move_data.move_paths.iter_enumerated() { - if let Some(parent) = move_path.parent { - all_facts.child_path.push((child, parent)); - } - } - - let fn_entry_start = - location_table.start_index(Location { block: START_BLOCK, statement_index: 0 }); - - // initialized_at - for init in move_data.inits.iter() { - match init.location { - InitLocation::Statement(location) => { - let block_data = &body[location.block]; - let is_terminator = location.statement_index == block_data.statements.len(); - - if is_terminator && init.kind == InitKind::NonPanicPathOnly { - // We are at the terminator of an init that has a panic path, - // and where the init should not happen on panic - - for successor in block_data.terminator().successors() { - if body[successor].is_cleanup { - continue; - } - - // The initialization happened in (or rather, when arriving at) - // the successors, but not in the unwind block. - let first_statement = Location { block: successor, statement_index: 0 }; - all_facts - .path_assigned_at_base - .push((init.path, location_table.start_index(first_statement))); - } - } else { - // In all other cases, the initialization just happens at the - // midpoint, like any other effect. - all_facts - .path_assigned_at_base - .push((init.path, location_table.mid_index(location))); - } - } - // Arguments are initialized on function entry - InitLocation::Argument(local) => { - assert!(body.local_kind(local) == LocalKind::Arg); - all_facts.path_assigned_at_base.push((init.path, fn_entry_start)); - } - } - } - - for (local, path) in move_data.rev_lookup.iter_locals_enumerated() { - if body.local_kind(local) != LocalKind::Arg { - // Non-arguments start out deinitialised; we simulate this with an - // initial move: - all_facts.path_moved_at_base.push((path, fn_entry_start)); - } - } - - // moved_out_at - // deinitialisation is assumed to always happen! - all_facts - .path_moved_at_base - .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); -} - -/// Emit universal regions facts, and their relations. -fn emit_universal_region_facts( - all_facts: &mut AllFacts, - borrow_set: &BorrowSet<'_>, - universal_region_relations: &UniversalRegionRelations<'_>, -) { - // 1: universal regions are modeled in Polonius as a pair: - // - the universal region vid itself. - // - a "placeholder loan" associated to this universal region. Since they don't exist in - // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index - // added to the existing number of loans, as if they succeeded them in the set. - // - let universal_regions = &universal_region_relations.universal_regions; - all_facts - .universal_region - .extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from)); - let borrow_count = borrow_set.len(); - debug!( - "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}", - universal_regions.len(), - borrow_count - ); - - for universal_region in universal_regions.universal_regions_iter() { - let universal_region_idx = universal_region.index(); - let placeholder_loan_idx = borrow_count + universal_region_idx; - all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into())); - } - - // 2: the universal region relations `outlives` constraints are emitted as - // `known_placeholder_subset` facts. - for (fr1, fr2) in universal_region_relations.known_outlives() { - if fr1 != fr2 { - debug!( - "emit_universal_region_facts: emitting polonius `known_placeholder_subset` \ - fr1={:?}, fr2={:?}", - fr1, fr2 - ); - all_facts.known_placeholder_subset.push((fr1.into(), fr2.into())); - } - } -} - -/// Emit facts about loan invalidations. -fn emit_loan_invalidations_facts<'tcx>( - all_facts: &mut AllFacts, - tcx: TyCtxt<'tcx>, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, -) { - loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set); -} - -/// Emit facts about CFG points and edges, as well as locations where loans are killed. -fn emit_cfg_and_loan_kills_facts<'tcx>( - all_facts: &mut AllFacts, - tcx: TyCtxt<'tcx>, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, -) { - loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set); -} +pub(crate) mod legacy; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 20d19a5375241..05e4a176a6d19 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -45,7 +45,7 @@ pub(super) fn generate<'a, 'tcx>( let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); - polonius::populate_access_facts(typeck, body, move_data); + polonius::emit_access_facts(typeck, body, move_data); trace::trace( typeck, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index e8d8ae0850bcf..5ffba94ee6821 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -11,88 +11,19 @@ use crate::location::{LocationIndex, LocationTable}; type VarPointRelation = Vec<(Local, LocationIndex)>; type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>; -struct UseFactsExtractor<'a, 'tcx> { - var_defined_at: &'a mut VarPointRelation, - var_used_at: &'a mut VarPointRelation, - location_table: &'a LocationTable, - var_dropped_at: &'a mut VarPointRelation, - move_data: &'a MoveData<'tcx>, - path_accessed_at_base: &'a mut PathPointRelation, -} - -// A Visitor to walk through the MIR and extract point-wise facts -impl<'tcx> UseFactsExtractor<'_, 'tcx> { - fn location_to_index(&self, location: Location) -> LocationIndex { - self.location_table.mid_index(location) - } - - fn insert_def(&mut self, local: Local, location: Location) { - debug!("UseFactsExtractor::insert_def()"); - self.var_defined_at.push((local, self.location_to_index(location))); - } - - fn insert_use(&mut self, local: Local, location: Location) { - debug!("UseFactsExtractor::insert_use()"); - self.var_used_at.push((local, self.location_to_index(location))); - } - - fn insert_drop_use(&mut self, local: Local, location: Location) { - debug!("UseFactsExtractor::insert_drop_use()"); - self.var_dropped_at.push((local, self.location_to_index(location))); - } - - fn insert_path_access(&mut self, path: MovePathIndex, location: Location) { - debug!("UseFactsExtractor::insert_path_access({:?}, {:?})", path, location); - self.path_accessed_at_base.push((path, self.location_to_index(location))); - } - - fn place_to_mpi(&self, place: &Place<'tcx>) -> Option { - match self.move_data.rev_lookup.find(place.as_ref()) { - LookupResult::Exact(mpi) => Some(mpi), - LookupResult::Parent(mmpi) => mmpi, - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for UseFactsExtractor<'a, 'tcx> { - fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { - match def_use::categorize(context) { - Some(DefUse::Def) => self.insert_def(local, location), - Some(DefUse::Use) => self.insert_use(local, location), - Some(DefUse::Drop) => self.insert_drop_use(local, location), - _ => (), - } - } - - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - self.super_place(place, context, location); - match context { - PlaceContext::NonMutatingUse(_) => { - if let Some(mpi) = self.place_to_mpi(place) { - self.insert_path_access(mpi, location); - } - } - - PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - if let Some(mpi) = self.place_to_mpi(place) { - self.insert_path_access(mpi, location); - } - } - _ => (), - } - } -} - -pub(super) fn populate_access_facts<'a, 'tcx>( +/// Emit polonius facts for variable defs, uses, drops, and path accesses. +pub(super) fn emit_access_facts<'a, 'tcx>( typeck: &mut TypeChecker<'a, 'tcx>, body: &Body<'tcx>, move_data: &MoveData<'tcx>, ) { if let Some(facts) = typeck.all_facts.as_mut() { - debug!("populate_access_facts()"); + debug!("emit_access_facts()"); + + let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); let location_table = typeck.location_table; - let mut extractor = UseFactsExtractor { + let mut extractor = AccessFactsExtractor { var_defined_at: &mut facts.var_defined_at, var_used_at: &mut facts.var_used_at, var_dropped_at: &mut facts.var_dropped_at, @@ -107,7 +38,6 @@ pub(super) fn populate_access_facts<'a, 'tcx>( "add use_of_var_derefs_origin facts - local={:?}, type={:?}", local, local_decl.ty ); - let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); let universal_regions = &typeck.universal_regions; typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| { let region_vid = universal_regions.to_region_vid(region); @@ -119,12 +49,12 @@ pub(super) fn populate_access_facts<'a, 'tcx>( /// For every potentially drop()-touched region `region` in `local`'s type /// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact. -pub(super) fn add_drop_of_var_derefs_origin<'tcx>( +pub(super) fn emit_drop_facts<'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, local: Local, kind: &GenericArg<'tcx>, ) { - debug!("add_drop_of_var_derefs_origin(local={:?}, kind={:?}", local, kind); + debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind); if let Some(facts) = typeck.all_facts.as_mut() { let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); let universal_regions = &typeck.universal_regions; @@ -134,3 +64,60 @@ pub(super) fn add_drop_of_var_derefs_origin<'tcx>( }); } } + +/// MIR visitor extracting point-wise facts about accesses. +struct AccessFactsExtractor<'a, 'tcx> { + var_defined_at: &'a mut VarPointRelation, + var_used_at: &'a mut VarPointRelation, + location_table: &'a LocationTable, + var_dropped_at: &'a mut VarPointRelation, + move_data: &'a MoveData<'tcx>, + path_accessed_at_base: &'a mut PathPointRelation, +} + +impl<'tcx> AccessFactsExtractor<'_, 'tcx> { + fn location_to_index(&self, location: Location) -> LocationIndex { + self.location_table.mid_index(location) + } +} + +impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> { + fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { + match def_use::categorize(context) { + Some(DefUse::Def) => { + debug!("AccessFactsExtractor - emit def"); + self.var_defined_at.push((local, self.location_to_index(location))); + } + Some(DefUse::Use) => { + debug!("AccessFactsExtractor - emit use"); + self.var_used_at.push((local, self.location_to_index(location))); + } + Some(DefUse::Drop) => { + debug!("AccessFactsExtractor - emit drop"); + self.var_dropped_at.push((local, self.location_to_index(location))); + } + _ => (), + } + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + self.super_place(place, context, location); + + match context { + PlaceContext::NonMutatingUse(_) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { + let path = match self.move_data.rev_lookup.find(place.as_ref()) { + LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path, + _ => { + // There's no path access to emit. + return; + } + }; + debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})"); + self.path_accessed_at_base.push((path, self.location_to_index(location))); + } + + _ => {} + } + } +} diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 3ec36c16cbfc5..539d3f97a6387 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -590,7 +590,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { Self::make_all_regions_live(self.elements, self.typeck, kind, live_at); - polonius::add_drop_of_var_derefs_origin(self.typeck, dropped_local, &kind); + polonius::emit_drop_facts(self.typeck, dropped_local, &kind); } }