From 6054a33bf2f9b03db0f4123aacdf4fe9a44dd20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 09:57:33 +0000 Subject: [PATCH 1/7] split polonius context into per-phase data - describe how that data flows during borrowck - prepares for recording some liveness data for diagnostics, not just for the main analysis --- .../src/polonius/liveness_constraints.rs | 24 +-------- compiler/rustc_borrowck/src/polonius/mod.rs | 53 ++++++++++++++++--- .../src/type_check/liveness/mod.rs | 14 ++--- .../src/type_check/liveness/trace.rs | 4 +- compiler/rustc_borrowck/src/type_check/mod.rs | 25 +++++---- 5 files changed, 70 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index 75ee29c9d0d50..6ab09f731c078 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -1,7 +1,6 @@ use std::collections::BTreeMap; use rustc_index::bit_set::SparseBitMatrix; -use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable}; @@ -9,12 +8,12 @@ use rustc_mir_dataflow::points::PointIndex; use super::{ ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, - PoloniusContext, + PoloniusLivenessContext, }; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; -impl PoloniusContext { +impl PoloniusLivenessContext { /// Record the variance of each region contained within the given value. pub(crate) fn record_live_region_variance<'tcx>( &mut self, @@ -30,25 +29,6 @@ impl PoloniusContext { }; extractor.relate(value, value).expect("Can't have a type error relating to itself"); } - - /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we - /// need to transpose the "points where each region is live" matrix to a "live regions per point" - /// matrix. - // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of - // borrowck. - pub(crate) fn record_live_regions_per_point( - &mut self, - num_regions: usize, - points_per_live_region: &SparseIntervalMatrix, - ) { - let mut live_regions_per_point = SparseBitMatrix::new(num_regions); - for region in points_per_live_region.rows() { - for point in points_per_live_region.row(region).unwrap().iter() { - live_regions_per_point.insert(point, region); - } - } - self.live_regions = Some(live_regions_per_point); - } } /// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 502c868194af2..11414a5719d86 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -32,6 +32,16 @@ //! - //! - //! +//! +//! Data flows like this: +//! 1) during MIR typeck, record liveness data needed later: live region variances, as well as the +//! usual NLL liveness data (just computed on more locals). That's the [PoloniusLivenessContext]. +//! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to +//! the polonius shape. That's the main [PoloniusContext]. +//! 3) during region inference, that data and the NLL outlives constraints are used to create the +//! localized outlives constraints, as described above. +//! 4) transfer these constraints back to the main borrowck procedure: it handles computing errors +//! and diagnostics, debugging and MIR dumping concerns. mod constraints; mod dump; @@ -43,6 +53,7 @@ mod typeck_constraints; use std::collections::BTreeMap; use rustc_index::bit_set::SparseBitMatrix; +use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::Body; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::points::PointIndex; @@ -57,11 +68,21 @@ use crate::{BorrowSet, RegionInferenceContext}; pub(crate) type LiveLoans = SparseBitMatrix; -/// This struct holds the data needed to create the Polonius localized constraints. +/// This struct holds the liveness data created during MIR typeck, and which will be used later in +/// the process, to compute the polonius localized constraints. +#[derive(Default)] +pub(crate) struct PoloniusLivenessContext { + /// The expected edge direction per live region: the kind of directed edge we'll create as + /// liveness constraints depends on the variance of types with respect to each contained region. + live_region_variances: BTreeMap, +} + +/// This struct holds the data needed to create the Polonius localized constraints. Its data is +/// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck. pub(crate) struct PoloniusContext { /// The set of regions that are live at a given point in the CFG, used to create localized /// outlives constraints between regions that are live at connected points in the CFG. - live_regions: Option>, + live_regions: SparseBitMatrix, /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. @@ -83,8 +104,27 @@ enum ConstraintDirection { } impl PoloniusContext { - pub(crate) fn new() -> PoloniusContext { - Self { live_region_variances: BTreeMap::new(), live_regions: None } + /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we + /// need to transpose the "points where each region is live" matrix to a "live regions per point" + /// matrix. + // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of + // borrowck. + pub(crate) fn create_from_liveness( + liveness_context: PoloniusLivenessContext, + num_regions: usize, + points_per_live_region: &SparseIntervalMatrix, + ) -> PoloniusContext { + let mut live_regions_per_point = SparseBitMatrix::new(num_regions); + for region in points_per_live_region.rows() { + for point in points_per_live_region.row(region).unwrap().iter() { + live_regions_per_point.insert(point, region); + } + } + + PoloniusContext { + live_regions: live_regions_per_point, + live_region_variances: liveness_context.live_region_variances, + } } /// Computes live loans using the set of loans model for `-Zpolonius=next`. @@ -112,13 +152,10 @@ impl PoloniusContext { &mut localized_outlives_constraints, ); - let live_regions = self.live_regions.as_ref().expect( - "live regions per-point data should have been created at the end of MIR typeck", - ); create_liveness_constraints( body, regioncx.liveness_constraints(), - live_regions, + &self.live_regions, &self.live_region_variances, regioncx.universal_regions(), &mut localized_outlives_constraints, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 4e0b2a4e29681..23b8bbb38e8b0 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -14,7 +14,7 @@ use tracing::debug; use super::TypeChecker; use crate::constraints::OutlivesConstraintSet; -use crate::polonius::PoloniusContext; +use crate::polonius::PoloniusLivenessContext; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; @@ -70,7 +70,7 @@ pub(super) fn generate<'a, 'tcx>( typeck.tcx(), &mut typeck.constraints.liveness_constraints, &typeck.universal_regions, - &mut typeck.polonius_context, + &mut typeck.polonius_liveness, body, ); } @@ -147,11 +147,11 @@ fn record_regular_live_regions<'tcx>( tcx: TyCtxt<'tcx>, liveness_constraints: &mut LivenessValues, universal_regions: &UniversalRegions<'tcx>, - polonius_context: &mut Option, + polonius_liveness: &mut Option, body: &Body<'tcx>, ) { let mut visitor = - LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_context }; + LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_liveness }; for (bb, data) in body.basic_blocks.iter_enumerated() { visitor.visit_basic_block_data(bb, data); } @@ -162,7 +162,7 @@ struct LiveVariablesVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, liveness_constraints: &'a mut LivenessValues, universal_regions: &'a UniversalRegions<'tcx>, - polonius_context: &'a mut Option, + polonius_liveness: &'a mut Option, } impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> { @@ -214,8 +214,8 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> { }); // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_context) = self.polonius_context { - polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value); + if let Some(polonius_liveness) = self.polonius_liveness { + polonius_liveness.record_live_region_variance(self.tcx, self.universal_regions, value); } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index c564d85616e25..62d49a62744e7 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -580,8 +580,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { }); // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_context) = typeck.polonius_context { - polonius_context.record_live_region_variance( + if let Some(polonius_liveness) = typeck.polonius_liveness.as_mut() { + polonius_liveness.record_live_region_variance( typeck.infcx.tcx, typeck.universal_regions, value, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index a2ef5588f48f8..45120324a0c0e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -48,8 +48,8 @@ use crate::borrow_set::BorrowSet; use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; use crate::diagnostics::UniverseInfo; use crate::member_constraints::MemberConstraintSet; -use crate::polonius::PoloniusContext; use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable}; +use crate::polonius::{PoloniusContext, PoloniusLivenessContext}; use crate::region_infer::TypeTest; use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices}; use crate::renumber::RegionCtxt; @@ -148,8 +148,8 @@ pub(crate) fn type_check<'a, 'tcx>( debug!(?normalized_inputs_and_output); - let mut polonius_context = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { - Some(PoloniusContext::new()) + let polonius_liveness = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { + Some(PoloniusLivenessContext::default()) } else { None }; @@ -168,7 +168,7 @@ pub(crate) fn type_check<'a, 'tcx>( polonius_facts, borrow_set, constraints: &mut constraints, - polonius_context: &mut polonius_context, + polonius_liveness, }; typeck.check_user_type_annotations(); @@ -185,11 +185,14 @@ pub(crate) fn type_check<'a, 'tcx>( let opaque_type_values = opaque_types::take_opaques_and_register_member_constraints(&mut typeck); - if let Some(polonius_context) = typeck.polonius_context.as_mut() { - let num_regions = infcx.num_region_vars(); - let points_per_live_region = typeck.constraints.liveness_constraints.points(); - polonius_context.record_live_regions_per_point(num_regions, points_per_live_region); - } + // We're done with typeck, we can finalize the polonius liveness context for region inference. + let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| { + PoloniusContext::create_from_liveness( + liveness_context, + infcx.num_region_vars(), + typeck.constraints.liveness_constraints.points(), + ) + }); MirTypeckResults { constraints, @@ -564,8 +567,8 @@ struct TypeChecker<'a, 'tcx> { polonius_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - /// When using `-Zpolonius=next`, the helper data used to create polonius constraints. - polonius_context: &'a mut Option, + /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. + polonius_liveness: Option, } /// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions From a8fc140848ccd1700fb1a403ef6d12e646b1f556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 10:37:40 +0000 Subject: [PATCH 2/7] create context for errors and diagnostics for last borrowck phase --- compiler/rustc_borrowck/src/lib.rs | 4 ++-- compiler/rustc_borrowck/src/nll.rs | 11 ++++++----- compiler/rustc_borrowck/src/polonius/dump.rs | 12 +++++++----- compiler/rustc_borrowck/src/polonius/mod.rs | 18 +++++++++++++----- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index decfab502bb59..c6caae45b7b28 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -198,7 +198,7 @@ fn do_mir_borrowck<'tcx>( polonius_output, opt_closure_req, nll_errors, - localized_outlives_constraints, + polonius_diagnostics, } = nll::compute_regions( &infcx, free_regions, @@ -329,7 +329,7 @@ fn do_mir_borrowck<'tcx>( body, ®ioncx, &borrow_set, - localized_outlives_constraints, + polonius_diagnostics, &opt_closure_req, ); diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 35264bd1a7075..1da6a596cfb58 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -27,7 +27,7 @@ use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; use crate::consumers::ConsumerOptions; use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors}; -use crate::polonius::LocalizedOutlivesConstraintSet; +use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, }; @@ -46,8 +46,9 @@ pub(crate) struct NllOutput<'tcx> { pub opt_closure_req: Option>, pub nll_errors: RegionErrors<'tcx>, - /// When using `-Zpolonius=next`: the localized typeck and liveness constraints. - pub localized_outlives_constraints: Option, + /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g. + /// localized typeck and liveness constraints. + pub polonius_diagnostics: Option, } /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal @@ -144,7 +145,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints // and use them to compute loan liveness. - let localized_outlives_constraints = polonius_context.as_ref().map(|polonius_context| { + let polonius_diagnostics = polonius_context.as_ref().map(|polonius_context| { polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set) }); @@ -188,7 +189,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( polonius_output, opt_closure_req: closure_region_requirements, nll_errors, - localized_outlives_constraints, + polonius_diagnostics, } } diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 6d32ee17f4c2a..b742a12ecf949 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -12,7 +12,9 @@ use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; -use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::polonius::{ + LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext, +}; use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; @@ -23,7 +25,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: Option, + polonius_diagnostics: Option, closure_region_requirements: &Option>, ) { let tcx = infcx.tcx; @@ -35,8 +37,8 @@ pub(crate) fn dump_polonius_mir<'tcx>( return; } - let localized_outlives_constraints = localized_outlives_constraints - .expect("missing localized constraints with `-Zpolonius=next`"); + let polonius_diagnostics_context = + polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`"); let _: io::Result<()> = try { let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?; @@ -45,7 +47,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( body, regioncx, borrow_set, - localized_outlives_constraints, + polonius_diagnostics_context.localized_outlives_constraints, closure_region_requirements, &mut file, )?; diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 11414a5719d86..82f5752f3623a 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -39,9 +39,9 @@ //! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to //! the polonius shape. That's the main [PoloniusContext]. //! 3) during region inference, that data and the NLL outlives constraints are used to create the -//! localized outlives constraints, as described above. -//! 4) transfer these constraints back to the main borrowck procedure: it handles computing errors -//! and diagnostics, debugging and MIR dumping concerns. +//! localized outlives constraints, as described above. That's the [PoloniusDiagnosticsContext]. +//! 4) transfer this back to the main borrowck procedure: it handles computing errors and +//! diagnostics, debugging and MIR dumping concerns. mod constraints; mod dump; @@ -89,6 +89,12 @@ pub(crate) struct PoloniusContext { live_region_variances: BTreeMap, } +/// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is +/// computed from the [PoloniusContext] when computing NLL regions. +pub(crate) struct PoloniusDiagnosticsContext { + localized_outlives_constraints: LocalizedOutlivesConstraintSet, +} + /// The direction a constraint can flow into. Used to create liveness constraints according to /// variance. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -135,13 +141,15 @@ impl PoloniusContext { /// /// Then, this graph is traversed, and combined with kills, reachability is recorded as loan /// liveness, to be used by the loan scope and active loans computations. + /// + /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( &self, tcx: TyCtxt<'tcx>, regioncx: &mut RegionInferenceContext<'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, - ) -> LocalizedOutlivesConstraintSet { + ) -> PoloniusDiagnosticsContext { let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default(); convert_typeck_constraints( tcx, @@ -173,6 +181,6 @@ impl PoloniusContext { ); regioncx.record_live_loans(live_loans); - localized_outlives_constraints + PoloniusDiagnosticsContext { localized_outlives_constraints } } } From 6ce46a82cb158c612a909b9acb16882c16fbe970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 12:04:55 +0000 Subject: [PATCH 3/7] record boring locals in polonius context this is used in diagnostics to focus on relevant live locals to match NLL diagnostics --- .../src/diagnostics/explain_borrow.rs | 23 +++++++++++-- compiler/rustc_borrowck/src/lib.rs | 8 ++++- compiler/rustc_borrowck/src/nll.rs | 2 +- compiler/rustc_borrowck/src/polonius/dump.rs | 8 ++--- compiler/rustc_borrowck/src/polonius/mod.rs | 34 ++++++++++++------- .../src/type_check/liveness/mod.rs | 23 ++++++++----- 6 files changed, 69 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 2656e0bb6a452..eeba62bb14296 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -548,8 +548,25 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } + // NLL doesn't consider boring locals for liveness, and wouldn't encounter a + // `Cause::LiveVar` for such a local. Polonius can't avoid computing liveness for boring + // locals yet, and will encounter them when trying to explain why a borrow contains a given + // point. + // + // We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we + // ensure that we don't emit live boring locals as explanations. + let is_local_boring = |local| { + if let Some(polonius_diagnostics) = self.polonius_diagnostics { + polonius_diagnostics.boring_nll_locals.contains(&local) + } else { + assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled()); + + // Boring locals are never the cause of a borrow explanation in NLLs. + false + } + }; match find_use::find(body, regioncx, tcx, region_sub, use_location) { - Some(Cause::LiveVar(local, location)) => { + Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => { let span = body.source_info(location).span; let spans = self .move_spans(Place::from(local).as_ref(), location) @@ -592,7 +609,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } - None => { + Some(Cause::LiveVar(..)) | None => { + // Here, under NLL: no cause was found. Under polonius: no cause was found, or a + // boring local was found, which we ignore like NLLs do to match its diagnostics. if let Some(region) = self.to_error_region_vid(borrow_region_vid) { let (category, from_closure, span, region_name, path) = self.free_region_constraint_info(borrow_region_vid, region); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index c6caae45b7b28..fe904726c2891 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -60,6 +60,7 @@ use crate::diagnostics::{ use crate::path_utils::*; use crate::place_ext::PlaceExt; use crate::places_conflict::{PlaceConflictBias, places_conflict}; +use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput}; use crate::prefixes::PrefixSet; use crate::region_infer::RegionInferenceContext; @@ -270,6 +271,7 @@ fn do_mir_borrowck<'tcx>( polonius_output: None, move_errors: Vec::new(), diags_buffer, + polonius_diagnostics: polonius_diagnostics.as_ref(), }; struct MoveVisitor<'a, 'b, 'infcx, 'tcx> { ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>, @@ -308,6 +310,7 @@ fn do_mir_borrowck<'tcx>( polonius_output, move_errors: Vec::new(), diags_buffer, + polonius_diagnostics: polonius_diagnostics.as_ref(), }; // Compute and report region errors, if any. @@ -329,7 +332,7 @@ fn do_mir_borrowck<'tcx>( body, ®ioncx, &borrow_set, - polonius_diagnostics, + polonius_diagnostics.as_ref(), &opt_closure_req, ); @@ -579,6 +582,9 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>, move_errors: Vec>, + + /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics. + polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>, } // Check that: diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 1da6a596cfb58..2031579dfd50a 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -145,7 +145,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints // and use them to compute loan liveness. - let polonius_diagnostics = polonius_context.as_ref().map(|polonius_context| { + let polonius_diagnostics = polonius_context.map(|polonius_context| { polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set) }); diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index b742a12ecf949..5f9fa3612b8d1 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -25,7 +25,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - polonius_diagnostics: Option, + polonius_diagnostics: Option<&PoloniusDiagnosticsContext>, closure_region_requirements: &Option>, ) { let tcx = infcx.tcx; @@ -37,7 +37,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( return; } - let polonius_diagnostics_context = + let polonius_diagnostics = polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`"); let _: io::Result<()> = try { @@ -47,7 +47,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( body, regioncx, borrow_set, - polonius_diagnostics_context.localized_outlives_constraints, + &polonius_diagnostics.localized_outlives_constraints, closure_region_requirements, &mut file, )?; @@ -65,7 +65,7 @@ fn emit_polonius_dump<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: LocalizedOutlivesConstraintSet, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, closure_region_requirements: &Option>, out: &mut dyn io::Write, ) -> io::Result<()> { diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 82f5752f3623a..142ef8ba28eff 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -52,9 +52,10 @@ mod typeck_constraints; use std::collections::BTreeMap; +use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::SparseBitMatrix; use rustc_index::interval::SparseIntervalMatrix; -use rustc_middle::mir::Body; +use rustc_middle::mir::{Body, Local}; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::points::PointIndex; @@ -75,24 +76,33 @@ pub(crate) struct PoloniusLivenessContext { /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. live_region_variances: BTreeMap, + + /// The regions that outlive free regions are used to distinguish relevant live locals from + /// boring locals. A boring local is one whose type contains only such regions. Polonius + /// currently has more boring locals than NLLs so we record the latter to use in errors and + /// diagnostics, to focus on the locals we consider relevant and match NLL diagnostics. + pub(crate) boring_nll_locals: FxHashSet, } /// This struct holds the data needed to create the Polonius localized constraints. Its data is /// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck. pub(crate) struct PoloniusContext { + /// The liveness data we recorded during MIR typeck. + liveness_context: PoloniusLivenessContext, + /// The set of regions that are live at a given point in the CFG, used to create localized /// outlives constraints between regions that are live at connected points in the CFG. live_regions: SparseBitMatrix, - - /// The expected edge direction per live region: the kind of directed edge we'll create as - /// liveness constraints depends on the variance of types with respect to each contained region. - live_region_variances: BTreeMap, } /// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is /// computed from the [PoloniusContext] when computing NLL regions. pub(crate) struct PoloniusDiagnosticsContext { + /// The localized outlives constraints that were computed in the main analysis. localized_outlives_constraints: LocalizedOutlivesConstraintSet, + + /// The liveness data computed during MIR typeck: [PoloniusLivenessContext::boring_nll_locals]. + pub(crate) boring_nll_locals: FxHashSet, } /// The direction a constraint can flow into. Used to create liveness constraints according to @@ -127,10 +137,7 @@ impl PoloniusContext { } } - PoloniusContext { - live_regions: live_regions_per_point, - live_region_variances: liveness_context.live_region_variances, - } + PoloniusContext { live_regions: live_regions_per_point, liveness_context } } /// Computes live loans using the set of loans model for `-Zpolonius=next`. @@ -144,12 +151,15 @@ impl PoloniusContext { /// /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( - &self, + self, tcx: TyCtxt<'tcx>, regioncx: &mut RegionInferenceContext<'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, ) -> PoloniusDiagnosticsContext { + let PoloniusLivenessContext { live_region_variances, boring_nll_locals } = + self.liveness_context; + let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default(); convert_typeck_constraints( tcx, @@ -164,7 +174,7 @@ impl PoloniusContext { body, regioncx.liveness_constraints(), &self.live_regions, - &self.live_region_variances, + &live_region_variances, regioncx.universal_regions(), &mut localized_outlives_constraints, ); @@ -181,6 +191,6 @@ impl PoloniusContext { ); regioncx.record_live_loans(live_loans); - PoloniusDiagnosticsContext { localized_outlives_constraints } + PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 23b8bbb38e8b0..dcc179030020d 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -38,19 +38,24 @@ pub(super) fn generate<'a, 'tcx>( ) { debug!("liveness::generate"); + let mut free_regions = regions_that_outlive_free_regions( + typeck.infcx.num_region_vars(), + &typeck.universal_regions, + &typeck.constraints.outlives_constraints, + ); + // NLLs can avoid computing some liveness data here because its constraints are // location-insensitive, but that doesn't work in polonius: locals whose type contains a region // that outlives a free region are not necessarily live everywhere in a flow-sensitive setting, // unlike NLLs. - let free_regions = if !typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { - regions_that_outlive_free_regions( - typeck.infcx.num_region_vars(), - &typeck.universal_regions, - &typeck.constraints.outlives_constraints, - ) - } else { - typeck.universal_regions.universal_regions_iter().collect() - }; + // We do record these regions in the polonius context, since they're used to differentiate + // relevant and boring locals, which is a key distinction used later in diagnostics. + if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { + let (_, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); + typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals = + boring_locals.into_iter().collect(); + free_regions = typeck.universal_regions.universal_regions_iter().collect(); + } let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); From bb1385ca8bdacd092e4110d26176dbf12092eedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 12:47:04 +0000 Subject: [PATCH 4/7] merge duplicate issue-46589 tests also add explicit revisions for -Zpolonius=next --- src/tools/tidy/src/issues.txt | 1 - ...ue-46589.stderr => issue-46589.nll.stderr} | 2 +- tests/ui/nll/issue-46589.rs | 13 ++++---- tests/ui/nll/polonius/issue-46589.rs | 31 ------------------- 4 files changed, 8 insertions(+), 39 deletions(-) rename tests/ui/nll/{issue-46589.stderr => issue-46589.nll.stderr} (94%) delete mode 100644 tests/ui/nll/polonius/issue-46589.rs diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 839d23fb9a78c..17786ed6d2fdb 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -3146,7 +3146,6 @@ ui/nll/issue-97997.rs ui/nll/issue-98170.rs ui/nll/issue-98589-closures-relate-named-regions.rs ui/nll/issue-98693.rs -ui/nll/polonius/issue-46589.rs ui/nll/relate_tys/issue-48071.rs ui/nll/ty-outlives/issue-53789-1.rs ui/nll/ty-outlives/issue-53789-2.rs diff --git a/tests/ui/nll/issue-46589.stderr b/tests/ui/nll/issue-46589.nll.stderr similarity index 94% rename from tests/ui/nll/issue-46589.stderr rename to tests/ui/nll/issue-46589.nll.stderr index abf62aaa51088..dc80c1d08deca 100644 --- a/tests/ui/nll/issue-46589.stderr +++ b/tests/ui/nll/issue-46589.nll.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `**other` as mutable more than once at a time - --> $DIR/issue-46589.rs:23:21 + --> $DIR/issue-46589.rs:24:21 | LL | *other = match (*other).get_self() { | -------- first mutable borrow occurs here diff --git a/tests/ui/nll/issue-46589.rs b/tests/ui/nll/issue-46589.rs index 417d9cab657a1..10aff0d7b4ef9 100644 --- a/tests/ui/nll/issue-46589.rs +++ b/tests/ui/nll/issue-46589.rs @@ -1,8 +1,9 @@ -// This tests passes in Polonius mode, so is skipped in the automated compare-mode. -// We will manually check it passes in Polonius tests, as we can't have a test here -// which conditionally passes depending on a test revision/compile-flags. - -//@ ignore-compare-mode-polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius_next polonius +//@ [polonius_next] check-pass +//@ [polonius_next] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] compile-flags: -Zpolonius struct Foo; @@ -21,7 +22,7 @@ impl Foo { *other = match (*other).get_self() { Some(s) => s, None => (*other).new_self() - //~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499] + //[nll]~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499] }; let c = other; diff --git a/tests/ui/nll/polonius/issue-46589.rs b/tests/ui/nll/polonius/issue-46589.rs deleted file mode 100644 index af791f7e24ddd..0000000000000 --- a/tests/ui/nll/polonius/issue-46589.rs +++ /dev/null @@ -1,31 +0,0 @@ -// This test is a copy of `ui/nll/issue-46589.rs` which fails in NLL but succeeds in Polonius. -// As we can't have a test here which conditionally passes depending on a test -// revision/compile-flags. We ensure here that it passes in Polonius mode. - -//@ check-pass -//@ compile-flags: -Z polonius - -struct Foo; - -impl Foo { - fn get_self(&mut self) -> Option<&mut Self> { - Some(self) - } - - fn new_self(&mut self) -> &mut Self { - self - } - - fn trigger_bug(&mut self) { - let other = &mut (&mut *self); - - *other = match (*other).get_self() { - Some(s) => s, - None => (*other).new_self() - }; - - let c = other; - } -} - -fn main() {} From e2d16419e8832a058ba451c382ee74f8ac4578f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 12:54:41 +0000 Subject: [PATCH 5/7] add explicit revisions to check-pass tests --- tests/ui/nll/polonius/assignment-kills-loans.rs | 5 ++++- tests/ui/nll/polonius/call-kills-loans.rs | 5 ++++- .../ui/nll/polonius/location-insensitive-scopes-liveness.rs | 6 ++++-- tests/ui/nll/polonius/storagedead-kills-loans.rs | 5 ++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/ui/nll/polonius/assignment-kills-loans.rs b/tests/ui/nll/polonius/assignment-kills-loans.rs index 182262e5c4b9b..56ea9c2f1f937 100644 --- a/tests/ui/nll/polonius/assignment-kills-loans.rs +++ b/tests/ui/nll/polonius/assignment-kills-loans.rs @@ -4,8 +4,11 @@ // facts only on simple assignments, but not projections, incorrectly causing errors to be emitted // for code accepted by NLL. They are all variations from example code in the NLL RFC. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius struct List { value: T, diff --git a/tests/ui/nll/polonius/call-kills-loans.rs b/tests/ui/nll/polonius/call-kills-loans.rs index c02293c9a78aa..0657c42f48cda 100644 --- a/tests/ui/nll/polonius/call-kills-loans.rs +++ b/tests/ui/nll/polonius/call-kills-loans.rs @@ -4,8 +4,11 @@ // by NLL but was incorrectly rejected by Polonius because of these // missing `killed` facts. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius struct Thing; diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs b/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs index cb56c8f6deb4c..677b7aa1df860 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs +++ b/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs @@ -5,9 +5,11 @@ // than `liveness::trace`, on some specific CFGs shapes: a variable was dead during tracing but its // regions were marked live later, and live loans were not recomputed at this point. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ revisions: nll polonius -//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius // minimized from wavefc-cli-3.0.0 fn repro1() { diff --git a/tests/ui/nll/polonius/storagedead-kills-loans.rs b/tests/ui/nll/polonius/storagedead-kills-loans.rs index 89cf919bb48e0..75a37e6ece575 100644 --- a/tests/ui/nll/polonius/storagedead-kills-loans.rs +++ b/tests/ui/nll/polonius/storagedead-kills-loans.rs @@ -3,8 +3,11 @@ // is correctly accepted by NLL but was incorrectly rejected by // Polonius because of these missing `killed` facts. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius use std::{io, mem}; use std::io::Read; From 18d417fdcfc372dc81244169be19c581499091ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 30 Jan 2025 13:03:56 +0000 Subject: [PATCH 6/7] add explicit revisions to polonius tests The blessed expectations were recently removed because they were only checked via the compare-mode. This switches to explicit revisions to ensure it doesn't happen again. - `assignment-to-differing-field` - `polonius-smoke-test` - `subset-relations` --- ...signment-to-differing-field.legacy.stderr} | 8 +-- ...ignment-to-differing-field.polonius.stderr | 51 ++++++++++++++++ .../polonius/assignment-to-differing-field.rs | 5 +- ...derr => polonius-smoke-test.legacy.stderr} | 8 +-- .../polonius-smoke-test.polonius.stderr | 59 +++++++++++++++++++ tests/ui/nll/polonius/polonius-smoke-test.rs | 8 ++- ....stderr => subset-relations.legacy.stderr} | 2 +- .../polonius/subset-relations.polonius.stderr | 14 +++++ tests/ui/nll/polonius/subset-relations.rs | 7 ++- 9 files changed, 148 insertions(+), 14 deletions(-) rename tests/ui/nll/polonius/{assignment-to-differing-field.stderr => assignment-to-differing-field.legacy.stderr} (92%) create mode 100644 tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr rename tests/ui/nll/polonius/{polonius-smoke-test.stderr => polonius-smoke-test.legacy.stderr} (91%) create mode 100644 tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr rename tests/ui/nll/polonius/{subset-relations.stderr => subset-relations.legacy.stderr} (92%) create mode 100644 tests/ui/nll/polonius/subset-relations.polonius.stderr diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr similarity index 92% rename from tests/ui/nll/polonius/assignment-to-differing-field.stderr rename to tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr index c46d010e4f576..cf5594dbd0786 100644 --- a/tests/ui/nll/polonius/assignment-to-differing-field.stderr +++ b/tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:20:21 + --> $DIR/assignment-to-differing-field.rs:23:21 | LL | fn assignment_to_field_projection<'a, T>( | -- lifetime `'a` defined here @@ -11,7 +11,7 @@ LL | return result; | ------ returning this value requires that `list.0.value` is borrowed for `'a` error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:23:26 + --> $DIR/assignment-to-differing-field.rs:26:26 | LL | fn assignment_to_field_projection<'a, T>( | -- lifetime `'a` defined here @@ -23,7 +23,7 @@ LL | list.1 = n; | ---------- assignment requires that `list.0.next` is borrowed for `'a` error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:37:21 + --> $DIR/assignment-to-differing-field.rs:40:21 | LL | fn assignment_through_projection_chain<'a, T>( | -- lifetime `'a` defined here @@ -35,7 +35,7 @@ LL | return result; | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` error[E0499]: cannot borrow `list.0.0.0.0.0.next` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:40:26 + --> $DIR/assignment-to-differing-field.rs:43:26 | LL | fn assignment_through_projection_chain<'a, T>( | -- lifetime `'a` defined here diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr new file mode 100644 index 0000000000000..cf5594dbd0786 --- /dev/null +++ b/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr @@ -0,0 +1,51 @@ +error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:23:21 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut (list.0).value); + | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:26:26 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = (list.0).next.as_mut() { + | ^^^^^^^^^^^^^ `list.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | list.1 = n; + | ---------- assignment requires that `list.0.next` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:40:21 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut ((((list.0).0).0).0).0.value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:43:26 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | *((((list.0).0).0).0).1 = n; + | --------------------------- assignment requires that `list.0.0.0.0.0.next` is borrowed for `'a` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.rs b/tests/ui/nll/polonius/assignment-to-differing-field.rs index fb6c956952543..9701cd2bb5e21 100644 --- a/tests/ui/nll/polonius/assignment-to-differing-field.rs +++ b/tests/ui/nll/polonius/assignment-to-differing-field.rs @@ -4,7 +4,10 @@ // that we do not kill too many borrows. Assignments to the `.1` // field projections should leave the borrows on `.0` intact. -//@ compile-flags: -Z polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy struct List { value: T, diff --git a/tests/ui/nll/polonius/polonius-smoke-test.stderr b/tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr similarity index 91% rename from tests/ui/nll/polonius/polonius-smoke-test.stderr rename to tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr index a8a8267290def..1268f6167f855 100644 --- a/tests/ui/nll/polonius/polonius-smoke-test.stderr +++ b/tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr @@ -1,11 +1,11 @@ error[E0515]: cannot return reference to local variable `x` - --> $DIR/polonius-smoke-test.rs:6:5 + --> $DIR/polonius-smoke-test.rs:10:5 | LL | &x | ^^ returns a reference to data owned by the current function error[E0503]: cannot use `x` because it was mutably borrowed - --> $DIR/polonius-smoke-test.rs:12:13 + --> $DIR/polonius-smoke-test.rs:16:13 | LL | let y = &mut x; | ------ `x` is borrowed here @@ -15,7 +15,7 @@ LL | let w = y; | - borrow later used here error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/polonius-smoke-test.rs:18:13 + --> $DIR/polonius-smoke-test.rs:22:13 | LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { | - - let's call the lifetime of this reference `'1` @@ -35,7 +35,7 @@ LL + let y = &mut x.clone(); | error[E0505]: cannot move out of `s` because it is borrowed - --> $DIR/polonius-smoke-test.rs:42:5 + --> $DIR/polonius-smoke-test.rs:46:5 | LL | let s = &mut 1; | - binding `s` declared here diff --git a/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr b/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr new file mode 100644 index 0000000000000..1268f6167f855 --- /dev/null +++ b/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr @@ -0,0 +1,59 @@ +error[E0515]: cannot return reference to local variable `x` + --> $DIR/polonius-smoke-test.rs:10:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/polonius-smoke-test.rs:16:13 + | +LL | let y = &mut x; + | ------ `x` is borrowed here +LL | let z = x; + | ^ use of borrowed `x` +LL | let w = y; + | - borrow later used here + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/polonius-smoke-test.rs:22:13 + | +LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { + | - - let's call the lifetime of this reference `'1` + | | + | binding `x` declared here +LL | let y = &mut *x; + | ------- borrow of `*x` occurs here +LL | let z = x; + | ^ move out of `x` occurs here +LL | y + | - returning this value requires that `*x` is borrowed for `'1` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = &mut *x; +LL + let y = &mut x.clone(); + | + +error[E0505]: cannot move out of `s` because it is borrowed + --> $DIR/polonius-smoke-test.rs:46:5 + | +LL | let s = &mut 1; + | - binding `s` declared here +LL | let r = &mut *s; + | ------- borrow of `*s` occurs here +LL | let tmp = foo(&r); +LL | s; + | ^ move out of `s` occurs here +LL | tmp; + | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let r = &mut *s; +LL + let r = &mut s.clone(); + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0503, E0505, E0515. +For more information about an error, try `rustc --explain E0503`. diff --git a/tests/ui/nll/polonius/polonius-smoke-test.rs b/tests/ui/nll/polonius/polonius-smoke-test.rs index ea5cdb263f5dd..7fbb3f9b47c3b 100644 --- a/tests/ui/nll/polonius/polonius-smoke-test.rs +++ b/tests/ui/nll/polonius/polonius-smoke-test.rs @@ -1,5 +1,9 @@ -// Check that Polonius borrow check works for simple cases. -//@ compile-flags: -Z polonius +// Check that Polonius works for simple cases. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy pub fn return_ref_to_local() -> &'static i32 { let x = 0; diff --git a/tests/ui/nll/polonius/subset-relations.stderr b/tests/ui/nll/polonius/subset-relations.legacy.stderr similarity index 92% rename from tests/ui/nll/polonius/subset-relations.stderr rename to tests/ui/nll/polonius/subset-relations.legacy.stderr index 9deca6449a8f1..10d42ca58d9df 100644 --- a/tests/ui/nll/polonius/subset-relations.stderr +++ b/tests/ui/nll/polonius/subset-relations.legacy.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/subset-relations.rs:10:5 + --> $DIR/subset-relations.rs:13:5 | LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { | -- -- lifetime `'b` defined here diff --git a/tests/ui/nll/polonius/subset-relations.polonius.stderr b/tests/ui/nll/polonius/subset-relations.polonius.stderr new file mode 100644 index 0000000000000..10d42ca58d9df --- /dev/null +++ b/tests/ui/nll/polonius/subset-relations.polonius.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/subset-relations.rs:13:5 + | +LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | y + | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: aborting due to 1 previous error + diff --git a/tests/ui/nll/polonius/subset-relations.rs b/tests/ui/nll/polonius/subset-relations.rs index 3c1af1983cfd0..d47e4e05533ee 100644 --- a/tests/ui/nll/polonius/subset-relations.rs +++ b/tests/ui/nll/polonius/subset-relations.rs @@ -3,7 +3,10 @@ // two free regions outlive each other, without any evidence that this // relation holds. -//@ compile-flags: -Z polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy // returning `y` requires that `'b: 'a`, but it's not known to be true fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { @@ -22,7 +25,7 @@ fn implied_bounds_subset<'a, 'b>(x: &'a &'b mut u32) -> &'a u32 { // `'b: 'a` is declared, and `'a: 'c` is known via implied bounds: // `'b: 'c` is therefore known to hold transitively -fn transitively_valid_subset<'a, 'b: 'a, 'c>(x: &'c &'a u32, y: &'b u32) -> &'c u32 { +fn transitively_valid_subset<'a, 'b: 'a, 'c>(x: &'c &'a u32, y: &'b u32) -> &'c u32 { y } From a616fa565745e2b8e8e245826f9452eb4c6d1191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Fri, 31 Jan 2025 09:10:15 +0000 Subject: [PATCH 7/7] update NLL `get_default` test for poloniuses - it still mentions AST borrowck - it tracks errors that shouldn't exist and are fixed by polonius --- tests/ui/nll/get_default.legacy.stderr | 18 ++++++++++++++++++ ...t_default.stderr => get_default.nll.stderr} | 10 +++++----- tests/ui/nll/get_default.polonius.stderr | 18 ++++++++++++++++++ tests/ui/nll/get_default.rs | 17 ++++++++++------- 4 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 tests/ui/nll/get_default.legacy.stderr rename tests/ui/nll/{get_default.stderr => get_default.nll.stderr} (87%) create mode 100644 tests/ui/nll/get_default.polonius.stderr diff --git a/tests/ui/nll/get_default.legacy.stderr b/tests/ui/nll/get_default.legacy.stderr new file mode 100644 index 0000000000000..699250312dc6b --- /dev/null +++ b/tests/ui/nll/get_default.legacy.stderr @@ -0,0 +1,18 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:35:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +LL | Some(v) => { +LL | map.set(String::new()); // We always expect an error here. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/nll/get_default.stderr b/tests/ui/nll/get_default.nll.stderr similarity index 87% rename from tests/ui/nll/get_default.stderr rename to tests/ui/nll/get_default.nll.stderr index af79771e7e1b9..9b5976ca7170e 100644 --- a/tests/ui/nll/get_default.stderr +++ b/tests/ui/nll/get_default.nll.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:21:17 + --> $DIR/get_default.rs:24:17 | LL | fn ok(map: &mut Map) -> &String { | - let's call the lifetime of this reference `'1` @@ -14,7 +14,7 @@ LL | map.set(String::new()); // Ideally, this would not error. | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:32:17 + --> $DIR/get_default.rs:35:17 | LL | fn err(map: &mut Map) -> &String { | - let's call the lifetime of this reference `'1` @@ -22,14 +22,14 @@ LL | loop { LL | match map.get() { | --- immutable borrow occurs here LL | Some(v) => { -LL | map.set(String::new()); // Both AST and MIR error here +LL | map.set(String::new()); // We always expect an error here. | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here LL | LL | return v; | - returning this value requires that `*map` is borrowed for `'1` error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:37:17 + --> $DIR/get_default.rs:40:17 | LL | fn err(map: &mut Map) -> &String { | - let's call the lifetime of this reference `'1` @@ -40,7 +40,7 @@ LL | match map.get() { LL | return v; | - returning this value requires that `*map` is borrowed for `'1` ... -LL | map.set(String::new()); // Ideally, just AST would error here +LL | map.set(String::new()); // Ideally, this would not error. | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here error: aborting due to 3 previous errors diff --git a/tests/ui/nll/get_default.polonius.stderr b/tests/ui/nll/get_default.polonius.stderr new file mode 100644 index 0000000000000..699250312dc6b --- /dev/null +++ b/tests/ui/nll/get_default.polonius.stderr @@ -0,0 +1,18 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:35:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +LL | Some(v) => { +LL | map.set(String::new()); // We always expect an error here. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/nll/get_default.rs b/tests/ui/nll/get_default.rs index ffac8a33da104..c3c09e9c2c553 100644 --- a/tests/ui/nll/get_default.rs +++ b/tests/ui/nll/get_default.rs @@ -1,7 +1,10 @@ // Basic test for free regions in the NLL code. This test ought to -// report an error due to a reborrowing constraint. Right now, we get -// a variety of errors from the older, AST-based machinery (notably -// borrowck), and then we get the NLL error at the end. +// report an error due to a reborrowing constraint. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy struct Map { } @@ -19,7 +22,7 @@ fn ok(map: &mut Map) -> &String { } None => { map.set(String::new()); // Ideally, this would not error. - //~^ ERROR borrowed as immutable + //[nll]~^ ERROR borrowed as immutable } } } @@ -29,13 +32,13 @@ fn err(map: &mut Map) -> &String { loop { match map.get() { Some(v) => { - map.set(String::new()); // Both AST and MIR error here + map.set(String::new()); // We always expect an error here. //~^ ERROR borrowed as immutable return v; } None => { - map.set(String::new()); // Ideally, just AST would error here - //~^ ERROR borrowed as immutable + map.set(String::new()); // Ideally, this would not error. + //[nll]~^ ERROR borrowed as immutable } } }