diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 548973714105b..d57432b64c40c 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -18,6 +18,7 @@ pub use super::polonius::legacy::{ }; pub use super::region_infer::RegionInferenceContext; use crate::BorrowCheckRootCtxt; +use crate::region_infer::InferredRegions; /// Struct used during mir borrowck to collect bodies with facts for a typeck root and all /// its nested bodies. @@ -89,9 +90,10 @@ pub struct BodyWithBorrowckFacts<'tcx> { pub promoted: IndexVec>, /// The set of borrows occurring in `body` with data about them. pub borrow_set: BorrowSet<'tcx>, - /// Context generated during borrowck, intended to be passed to + /// The inferred region values. These are included because they + /// are necessary as input to /// [`calculate_borrows_out_of_scope_at_location`]. - pub region_inference_context: RegionInferenceContext<'tcx>, + pub inferred_regions: InferredRegions<'tcx>, /// The table that maps Polonius points to locations in the table. /// Populated when using [`ConsumerOptions::PoloniusInputFacts`] /// or [`ConsumerOptions::PoloniusOutputFacts`]. diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 813ceaeb8da9f..4030e651e15e5 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -14,7 +14,9 @@ use rustc_mir_dataflow::impls::{ use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice}; use tracing::debug; -use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict}; +use crate::region_infer::InferredRegions; +use crate::region_infer::values::LivenessValues; +use crate::{BorrowSet, PlaceConflictBias, PlaceExt, places_conflict}; // This analysis is different to most others. Its results aren't computed with // `iterate_to_fixpoint`, but are instead composed from the results of three sub-analyses that are @@ -182,27 +184,25 @@ struct OutOfScopePrecomputer<'a, 'tcx> { visited: DenseBitSet, visit_stack: Vec, body: &'a Body<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, borrows_out_of_scope_at_location: FxIndexMap>, } impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { fn compute( body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + scc_values: &InferredRegions<'tcx>, borrow_set: &BorrowSet<'tcx>, ) -> FxIndexMap> { let mut prec = OutOfScopePrecomputer { visited: DenseBitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], body, - regioncx, borrows_out_of_scope_at_location: FxIndexMap::default(), }; for (borrow_index, borrow_data) in borrow_set.iter_enumerated() { let borrow_region = borrow_data.region; let location = borrow_data.reserve_location; - prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location); + prec.precompute_borrows_out_of_scope(scc_values, borrow_index, borrow_region, location); } prec.borrows_out_of_scope_at_location @@ -210,6 +210,7 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { fn precompute_borrows_out_of_scope( &mut self, + scc_values: &InferredRegions<'tcx>, borrow_index: BorrowIndex, borrow_region: RegionVid, first_location: Location, @@ -222,12 +223,9 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { let first_lo = first_location.statement_index; let first_hi = first_bb_data.statements.len(); - if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive( - borrow_region, - first_block, - first_lo, - first_hi, - ) { + if let Some(kill_stmt) = + scc_values.first_non_contained_inclusive(borrow_region, first_block, first_lo, first_hi) + { let kill_location = Location { block: first_block, statement_index: kill_stmt }; // If region does not contain a point at the location, then add to list and skip // successor locations. @@ -255,7 +253,7 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { let bb_data = &self.body[block]; let num_stmts = bb_data.statements.len(); if let Some(kill_stmt) = - self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts) + scc_values.first_non_contained_inclusive(borrow_region, block, 0, num_stmts) { let kill_location = Location { block, statement_index: kill_stmt }; // If region does not contain a point at the location, then add to list and skip @@ -285,25 +283,24 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. pub fn calculate_borrows_out_of_scope_at_location<'tcx>( body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + scc_values: &InferredRegions<'tcx>, borrow_set: &BorrowSet<'tcx>, ) -> FxIndexMap> { - OutOfScopePrecomputer::compute(body, regioncx, borrow_set) + OutOfScopePrecomputer::compute(body, scc_values, borrow_set) } struct PoloniusOutOfScopePrecomputer<'a, 'tcx> { visited: DenseBitSet, visit_stack: Vec, body: &'a Body<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, - + liveness_values: &'a LivenessValues, loans_out_of_scope_at_location: FxIndexMap>, } impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { fn compute( body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + liveness_values: &LivenessValues, borrow_set: &BorrowSet<'tcx>, ) -> FxIndexMap> { // The in-tree polonius analysis computes loans going out of scope using the @@ -312,7 +309,7 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { visited: DenseBitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], body, - regioncx, + liveness_values, loans_out_of_scope_at_location: FxIndexMap::default(), }; for (loan_idx, loan_data) in borrow_set.iter_enumerated() { @@ -412,7 +409,8 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { // Reachability is location-insensitive, and we could take advantage of that, by jumping // to a further point than just the next statement: we can jump to the furthest point // within the block where `r` is live. - if self.regioncx.is_loan_live_at(loan_idx, location) { + let point = self.liveness_values.point_from_location(location); + if self.liveness_values.is_loan_live_at(loan_idx, point) { continue; } @@ -429,14 +427,15 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { pub fn new( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + liveness_values: &'a LivenessValues, + scc_values: &InferredRegions<'tcx>, borrow_set: &'a BorrowSet<'tcx>, ) -> Self { let borrows_out_of_scope_at_location = if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { - calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set) + calculate_borrows_out_of_scope_at_location(body, scc_values, borrow_set) } else { - PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set) + PoloniusOutOfScopePrecomputer::compute(body, liveness_values, borrow_set) }; Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location } } diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 254d28d243ff2..fd9a97846b311 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -403,8 +403,8 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { // started MIR borrowchecking with, so the region // constraints have already been taken. Use the data from // our `mbcx` instead. - |vid| RegionVariableOrigin::Nll(mbcx.regioncx.definitions[vid].origin), - |vid| mbcx.regioncx.definitions[vid].universe, + |vid| RegionVariableOrigin::Nll(mbcx.definitions[vid].origin), + |vid| mbcx.definitions[vid].universe, ) } } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index caf6a86af098a..743c3ee98aeae 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3434,7 +3434,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let tcx = self.infcx.tcx; - let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; + let return_ty = self.universal_regions().unnormalized_output_ty; // to avoid panics if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index bbd0a8ae07108..40cd111b7b6d8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -21,7 +21,7 @@ use super::{RegionName, UseSpans, find_use}; use crate::borrow_set::BorrowData; use crate::constraints::OutlivesConstraint; use crate::nll::ConstraintDescription; -use crate::region_infer::{BlameConstraint, Cause}; +use crate::region_infer::{BlameConstraint, Cause, ConstraintSearch}; use crate::{MirBorrowckCtxt, WriteKind}; #[derive(Debug)] @@ -572,14 +572,23 @@ fn suggest_rewrite_if_let( } } -impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { +impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { + pub(crate) fn constraint_search<'mbc>(&'mbc self) -> ConstraintSearch<'mbc, 'tcx> { + ConstraintSearch { + definitions: self.definitions, + fr_static: self.universal_regions().fr_static, + constraint_graph: &self.constraint_graph, + constraints: &self.outlives_constraints, + } + } + fn free_region_constraint_info( &self, borrow_region: RegionVid, outlived_region: RegionVid, ) -> (ConstraintCategory<'tcx>, bool, Span, Option, Vec>) { - let (blame_constraint, path) = self.regioncx.best_blame_constraint( + let (blame_constraint, path) = self.constraint_search().best_blame_constraint( borrow_region, NllRegionVariableOrigin::FreeRegion, outlived_region, @@ -611,14 +620,17 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { borrow: &BorrowData<'tcx>, kind_place: Option<(WriteKind, Place<'tcx>)>, ) -> BorrowExplanation<'tcx> { - let regioncx = &self.regioncx; let body: &Body<'_> = self.body; let tcx = self.infcx.tcx; let borrow_region_vid = borrow.region; debug!(?borrow_region_vid); - let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location); + let mut region_sub = self.constraint_search().find_sub_region_live_at( + &self.liveness_constraints, + borrow_region_vid, + location, + ); debug!(?region_sub); let mut use_location = location; @@ -630,11 +642,13 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // loop iteration. In this case, try using the loop terminator location in // `find_sub_region_live_at`. if let Some(loop_terminator_location) = - regioncx.find_loop_terminator_location(borrow.region, body) + self.scc_values.find_loop_terminator_location(borrow.region, body) { - region_sub = self - .regioncx - .find_sub_region_live_at(borrow_region_vid, loop_terminator_location); + region_sub = self.constraint_search().find_sub_region_live_at( + &self.liveness_constraints, + borrow_region_vid, + loop_terminator_location, + ); debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub); use_location = loop_terminator_location; use_in_later_iteration_of_loop = true; @@ -658,7 +672,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { false } }; - match find_use::find(body, regioncx, tcx, region_sub, use_location) { + match find_use::find(body, self.scc_values, tcx, region_sub, use_location) { Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => { let span = body.source_info(location).span; let spans = self diff --git a/compiler/rustc_borrowck/src/diagnostics/find_use.rs b/compiler/rustc_borrowck/src/diagnostics/find_use.rs index 96f48840468e5..8ab79fbffd406 100644 --- a/compiler/rustc_borrowck/src/diagnostics/find_use.rs +++ b/compiler/rustc_borrowck/src/diagnostics/find_use.rs @@ -6,23 +6,23 @@ use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{RegionVid, TyCtxt}; use crate::def_use::{self, DefUse}; -use crate::region_infer::{Cause, RegionInferenceContext}; +use crate::region_infer::{Cause, InferredRegions}; pub(crate) fn find<'tcx>( body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + scc_values: &InferredRegions<'tcx>, tcx: TyCtxt<'tcx>, region_vid: RegionVid, start_point: Location, ) -> Option { - let mut uf = UseFinder { body, regioncx, tcx, region_vid, start_point }; + let mut uf = UseFinder { body, tcx, region_vid, start_point, scc_values: &scc_values }; uf.find() } struct UseFinder<'a, 'tcx> { body: &'a Body<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, + scc_values: &'a InferredRegions<'tcx>, tcx: TyCtxt<'tcx>, region_vid: RegionVid, start_point: Location, @@ -35,7 +35,7 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> { queue.push_back(self.start_point); while let Some(p) = queue.pop_front() { - if !self.regioncx.region_contains(self.region_vid, p) { + if !self.scc_values.region_contains(self.region_vid, p) { continue; } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index c15e7041c94d3..945970acf5b48 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -667,7 +667,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let tcx = self.infcx.tcx; let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| { let outlived = constraint.sub; - if let Some(origin) = self.regioncx.definitions.get(outlived) + if let Some(origin) = self.definitions.get(outlived) && let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin && let Some(id) = placeholder.bound.kind.get_id() && let Some(placeholder_id) = id.as_local() diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs index 62ba4d172a3f4..14768cfb33bc6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs @@ -16,9 +16,10 @@ use rustc_trait_selection::errors::impl_trait_overcapture_suggestion; use crate::MirBorrowckCtxt; use crate::borrow_set::BorrowData; -use crate::consumers::RegionInferenceContext; +use crate::region_infer::ConstraintSearch; use crate::region_infer::opaque_types::DeferredOpaqueTypeError; use crate::type_check::Locations; +use crate::universal_regions::UniversalRegions; impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn report_opaque_type_errors(&mut self, errors: Vec>) { @@ -42,12 +43,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { member_region, } => { let named_ty = - self.regioncx.name_regions_for_member_constraint(infcx.tcx, hidden_type.ty); - let named_key = self - .regioncx - .name_regions_for_member_constraint(infcx.tcx, opaque_type_key); + self.name_regions_for_member_constraint(infcx.tcx, hidden_type.ty); + let named_key = + self.name_regions_for_member_constraint(infcx.tcx, opaque_type_key); let named_region = - self.regioncx.name_regions_for_member_constraint(infcx.tcx, member_region); + self.name_regions_for_member_constraint(infcx.tcx, member_region); let diag = unexpected_hidden_region_diagnostic( infcx, self.mir_def_id(), @@ -107,9 +107,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let tcx = self.infcx.tcx; let ControlFlow::Break((opaque_def_id, offending_region_idx, location)) = ty .visit_with(&mut FindOpaqueRegion { - regioncx: &self.regioncx, tcx, borrow_region: borrow.region, + universal_regions: self.universal_regions(), + constraint_search: self.constraint_search(), }) else { continue; @@ -209,8 +210,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// This visitor contains the bulk of the logic for this lint. struct FindOpaqueRegion<'a, 'tcx> { tcx: TyCtxt<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, + constraint_search: ConstraintSearch<'a, 'tcx>, borrow_region: ty::RegionVid, + universal_regions: &'a UniversalRegions<'tcx>, } impl<'tcx> TypeVisitor> for FindOpaqueRegion<'_, 'tcx> { @@ -237,11 +239,11 @@ impl<'tcx> TypeVisitor> for FindOpaqueRegion<'_, 'tcx> { if opaque_region.is_bound() { continue; } - let opaque_region_vid = self.regioncx.to_region_vid(opaque_region); + let opaque_region_vid = self.universal_regions.to_region_vid(opaque_region); // Find a path between the borrow region and our opaque capture. if let Some(path) = self - .regioncx + .constraint_search .constraint_path_between_regions(self.borrow_region, opaque_region_vid) { for constraint in path { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 17f1988a17c40..46980724ed3a7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -148,20 +148,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// to find a good name from that. Returns `None` if we can't find /// one (e.g., this is just some random part of the CFG). pub(super) fn to_error_region(&self, r: RegionVid) -> Option> { - self.to_error_region_vid(r).and_then(|r| self.regioncx.region_definition(r).external_name) + self.to_error_region_vid(r).and_then(|r| self.definitions[r].external_name) } /// Returns the `RegionVid` corresponding to the region returned by /// `to_error_region`. pub(super) fn to_error_region_vid(&self, r: RegionVid) -> Option { - if self.regioncx.universal_regions().is_universal_region(r) { + if self.universal_regions().is_universal_region(r) { Some(r) } else { // We just want something nameable, even if it's not // actually an upper bound. - let upper_bound = self.regioncx.approx_universal_upper_bound(r); + let upper_bound = self.approx_universal_upper_bound(r); - if self.regioncx.upper_bound_in_region_scc(r, upper_bound) { + if self.scc_values.upper_bound_in_region_scc(r, upper_bound) { self.to_error_region_vid(upper_bound) } else { None @@ -185,7 +185,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(r) = self.to_error_region(fr) && let ty::ReLateParam(late_param) = r.kind() && let ty::LateParamRegionKind::ClosureEnv = late_param.kind - && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty + && let DefiningTy::Closure(_, args) = self.universal_regions().defining_ty { return args.as_closure().kind() == ty::ClosureKind::FnMut; } @@ -205,7 +205,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // find generic associated types in the given region 'lower_bound' let gat_id_and_generics = self - .regioncx + .scc_values .placeholders_contained_in(lower_bound) .map(|placeholder| { if let Some(id) = placeholder.bound.kind.get_id() @@ -365,11 +365,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { placeholder, error_element, } => { - let error_vid = self.regioncx.region_from_element(longer_fr, &error_element); + let error_vid = self.constraint_search().region_from_element( + self.liveness_constraints, + longer_fr, + &error_element, + self.definitions, + ); // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. let cause = self - .regioncx + .constraint_search() .best_blame_constraint( longer_fr, NllRegionVariableOrigin::Placeholder(placeholder), @@ -379,7 +384,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .cause; let universe = placeholder.universe; - let universe_info = self.regioncx.universe_info(universe); + let universe_info = self.universe_info(universe); universe_info.report_erroneous_element(self, placeholder, error_element, cause); } @@ -430,7 +435,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); let (blame_constraint, path) = - self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr); + self.constraint_search().best_blame_constraint(fr, fr_origin, outlived_fr); let BlameConstraint { category, cause, variance_info, .. } = blame_constraint; debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info); @@ -447,8 +452,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let (fr_is_local, outlived_fr_is_local): (bool, bool) = ( - self.regioncx.universal_regions().is_local_free_region(fr), - self.regioncx.universal_regions().is_local_free_region(outlived_fr), + self.universal_regions().is_local_free_region(fr), + self.universal_regions().is_local_free_region(outlived_fr), ); debug!( @@ -580,7 +585,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) -> Diag<'infcx> { let ErrorConstraintInfo { outlived_fr, span, .. } = errci; - let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; + let mut output_ty = self.universal_regions().unnormalized_output_ty; if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *output_ty.kind() { output_ty = self.infcx.tcx.type_of(def_id).instantiate_identity() }; @@ -603,7 +608,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut diag = self.dcx().create_err(err); if let ReturnConstraint::ClosureUpvar(upvar_field) = kind { - let def_id = match self.regioncx.universal_regions().defining_ty { + let def_id = match self.universal_regions().defining_ty { DefiningTy::Closure(def_id, _) => def_id, ty => bug!("unexpected DefiningTy {:?}", ty), }; @@ -649,14 +654,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'infcx> { let ErrorConstraintInfo { span, category, .. } = errci; - let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region( + let fr_name_and_span = self.universal_regions().get_var_name_and_span_for_region( self.infcx.tcx, self.body, &self.local_names(), &self.upvars, errci.fr, ); - let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region( + let outlived_fr_name_and_span = self.universal_regions().get_var_name_and_span_for_region( self.infcx.tcx, self.body, &self.local_names(), @@ -664,15 +669,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { errci.outlived_fr, ); - let escapes_from = - self.infcx.tcx.def_descr(self.regioncx.universal_regions().defining_ty.def_id()); + let escapes_from = self.infcx.tcx.def_descr(self.universal_regions().defining_ty.def_id()); // Revert to the normal error in these cases. // Assignments aren't "escapes" in function items. if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none()) || (*category == ConstraintCategory::Assignment - && self.regioncx.universal_regions().defining_ty.is_fn_def()) - || self.regioncx.universal_regions().defining_ty.is_const() + && self.universal_regions().defining_ty.is_fn_def()) + || self.universal_regions().defining_ty.is_const() { return self.report_general_error(errci); } @@ -773,7 +777,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { outlived_fr_name.highlight_region_name(&mut diag); let err_category = if matches!(category, ConstraintCategory::Return(_)) - && self.regioncx.universal_regions().is_local_free_region(*outlived_fr) + && self.universal_regions().is_local_free_region(*outlived_fr) { LifetimeReturnCategoryErr::WrongReturn { span: *span, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index fba0879e81337..9e4d1154d4fa0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -248,7 +248,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { self.next_region_name.try_borrow().unwrap() ); - assert!(self.regioncx.universal_regions().is_universal_region(fr)); + //FIXME! assert!(self.regioncx.universal_regions().is_universal_region(fr)); match self.region_names.borrow_mut().entry(fr) { IndexEntry::Occupied(precomputed_name) => Some(*precomputed_name.get()), @@ -324,7 +324,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } ty::LateParamRegionKind::ClosureEnv => { - let def_ty = self.regioncx.universal_regions().defining_ty; + let def_ty = self.universal_regions().defining_ty; let closure_kind = match def_ty { DefiningTy::Closure(_, args) => args.as_closure().kind(), @@ -385,12 +385,13 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let implicit_inputs = self.regioncx.universal_regions().defining_ty.implicit_inputs(); - let argument_index = self.regioncx.get_argument_index_for_region(self.infcx.tcx, fr)?; + let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs(); + let argument_index = + self.universal_regions().get_argument_index_for_region(self.infcx.tcx, fr)?; - let arg_ty = self.regioncx.universal_regions().unnormalized_input_tys - [implicit_inputs + argument_index]; - let (_, span) = self.regioncx.get_argument_name_and_span_for_region( + let arg_ty = + self.universal_regions().unnormalized_input_tys[implicit_inputs + argument_index]; + let (_, span) = self.universal_regions().get_argument_name_and_span_for_region( self.body, self.local_names(), argument_index, @@ -643,8 +644,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// ``` #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option { - let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?; - let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( + let upvar_index = + self.universal_regions().get_upvar_index_for_region(self.infcx.tcx, fr)?; + let (upvar_name, upvar_span) = self.universal_regions().get_upvar_name_and_span_for_region( self.infcx.tcx, self.upvars, upvar_index, @@ -665,7 +667,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option { let tcx = self.infcx.tcx; - let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; + let return_ty = self.universal_regions().unnormalized_output_ty; debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty); if !tcx.any_free_region_meets(&return_ty, |r| r.as_var() == fr) { return None; @@ -848,7 +850,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { ) -> Option { // Note: coroutines from `async fn` yield `()`, so we don't have to // worry about them here. - let yield_ty = self.regioncx.universal_regions().yield_ty?; + let yield_ty = self.universal_regions().yield_ty?; debug!("give_name_if_anonymous_region_appears_in_yield_ty: yield_ty = {:?}", yield_ty); let tcx = self.infcx.tcx; @@ -939,18 +941,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { .predicates; if let Some(upvar_index) = self - .regioncx .universal_regions() .defining_ty .upvar_tys() .iter() .position(|ty| self.any_param_predicate_mentions(&predicates, ty, region)) { - let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( - self.infcx.tcx, - self.upvars, - upvar_index, - ); + let (upvar_name, upvar_span) = self + .universal_regions() + .get_upvar_name_and_span_for_region(self.infcx.tcx, self.upvars, upvar_index); let region_name = self.synthesize_region_name(); Some(RegionName { @@ -958,17 +957,14 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), }) } else if let Some(arg_index) = self - .regioncx .universal_regions() .unnormalized_input_tys .iter() .position(|ty| self.any_param_predicate_mentions(&predicates, *ty, region)) { - let (arg_name, arg_span) = self.regioncx.get_argument_name_and_span_for_region( - self.body, - self.local_names(), - arg_index, - ); + let (arg_name, arg_span) = self + .universal_regions() + .get_argument_name_and_span_for_region(self.body, self.local_names(), arg_index); let region_name = self.synthesize_region_name(); Some(RegionName { diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index 14ed6a27a7a15..7acfa2740b700 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -4,9 +4,9 @@ use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_span::{Span, Symbol}; use tracing::debug; -use crate::region_infer::RegionInferenceContext; +use crate::universal_regions::UniversalRegions; -impl<'tcx> RegionInferenceContext<'tcx> { +impl<'tcx> UniversalRegions<'tcx> { pub(crate) fn get_var_name_and_span_for_region( &self, tcx: TyCtxt<'tcx>, @@ -16,7 +16,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr: RegionVid, ) -> Option<(Option, Span)> { debug!("get_var_name_and_span_for_region(fr={fr:?})"); - assert!(self.universal_regions().is_universal_region(fr)); + assert!(self.is_universal_region(fr)); debug!("get_var_name_and_span_for_region: attempting upvar"); self.get_upvar_index_for_region(tcx, fr) @@ -39,17 +39,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { tcx: TyCtxt<'tcx>, fr: RegionVid, ) -> Option { - let upvar_index = - self.universal_regions().defining_ty.upvar_tys().iter().position(|upvar_ty| { - debug!("get_upvar_index_for_region: upvar_ty={upvar_ty:?}"); - tcx.any_free_region_meets(&upvar_ty, |r| { - let r = r.as_var(); - debug!("get_upvar_index_for_region: r={r:?} fr={fr:?}"); - r == fr - }) - })?; + let upvar_index = self.defining_ty.upvar_tys().iter().position(|upvar_ty| { + debug!("get_upvar_index_for_region: upvar_ty={upvar_ty:?}"); + tcx.any_free_region_meets(&upvar_ty, |r| { + let r = r.as_var(); + debug!("get_upvar_index_for_region: r={r:?} fr={fr:?}"); + r == fr + }) + })?; - let upvar_ty = self.universal_regions().defining_ty.upvar_tys().get(upvar_index); + let upvar_ty = self.defining_ty.upvar_tys().get(upvar_index); debug!( "get_upvar_index_for_region: found {fr:?} in upvar {upvar_index} which has type {upvar_ty:?}", @@ -88,18 +87,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { tcx: TyCtxt<'tcx>, fr: RegionVid, ) -> Option { - let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs(); + let implicit_inputs = self.defining_ty.implicit_inputs(); let argument_index = - self.universal_regions().unnormalized_input_tys.iter().skip(implicit_inputs).position( - |arg_ty| { - debug!("get_argument_index_for_region: arg_ty = {arg_ty:?}"); - tcx.any_free_region_meets(arg_ty, |r| r.as_var() == fr) - }, - )?; + self.unnormalized_input_tys.iter().skip(implicit_inputs).position(|arg_ty| { + debug!("get_argument_index_for_region: arg_ty = {arg_ty:?}"); + tcx.any_free_region_meets(arg_ty, |r| r.as_var() == fr) + })?; debug!( "get_argument_index_for_region: found {fr:?} in argument {argument_index} which has type {:?}", - self.universal_regions().unnormalized_input_tys[argument_index], + self.unnormalized_input_tys[argument_index], ); Some(argument_index) @@ -113,7 +110,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { local_names: &IndexSlice>, argument_index: usize, ) -> (Option, Span) { - let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs(); + let implicit_inputs = self.defining_ty.implicit_inputs(); let argument_local = Local::from_usize(implicit_inputs + argument_index + 1); debug!("get_argument_name_and_span_for_region: argument_local={argument_local:?}"); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4a059481c326b..1ebf5e4914fa8 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -54,10 +54,13 @@ use smallvec::SmallVec; use tracing::{debug, instrument}; use crate::borrow_set::{BorrowData, BorrowSet}; +use crate::constraints::OutlivesConstraintSet; +use crate::constraints::graph::NormalConstraintGraph; use crate::consumers::{BodyWithBorrowckFacts, RustcFacts}; use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows}; use crate::diagnostics::{ AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName, + UniverseInfo, }; use crate::path_utils::*; use crate::place_ext::PlaceExt; @@ -67,12 +70,14 @@ use crate::polonius::legacy::{ }; use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext}; use crate::prefixes::PrefixSet; -use crate::region_infer::RegionInferenceContext; use crate::region_infer::opaque_types::DeferredOpaqueTypeError; +use crate::region_infer::values::LivenessValues; +use crate::region_infer::{InferredRegions, RegionDefinition, RegionInferenceContext}; use crate::renumber::RegionCtxt; use crate::session_diagnostics::VarNeedNotMut; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults}; +use crate::universal_regions::UniversalRegions; mod borrow_set; mod borrowck_errors; @@ -415,12 +420,16 @@ fn borrowck_check_region_constraints<'tcx>( // Compute non-lexical lifetimes using the constraints computed // by typechecking the MIR body. let nll::NllOutput { - regioncx, + scc_values, polonius_input, polonius_output, opt_closure_req, nll_errors, polonius_diagnostics, + definitions, + liveness_constraints, + outlives_constraints, + universe_causes, } = nll::compute_regions( root_cx, &infcx, @@ -429,7 +438,7 @@ fn borrowck_check_region_constraints<'tcx>( &move_data, &borrow_set, location_map, - universal_region_relations, + &universal_region_relations, constraints, polonius_facts, polonius_context, @@ -437,19 +446,38 @@ fn borrowck_check_region_constraints<'tcx>( // Dump MIR results into a file, if that is enabled. This lets us // write unit-tests, as well as helping with debugging. - nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set); + nll::dump_nll_mir( + &infcx, + body, + &scc_values, + &opt_closure_req, + &borrow_set, + &definitions, + &universal_region_relations, + &outlives_constraints, + &liveness_constraints, + ); polonius::dump_polonius_mir( &infcx, body, - ®ioncx, + &scc_values, &opt_closure_req, &borrow_set, + &definitions, + &liveness_constraints, + &outlives_constraints, + &universal_region_relations, polonius_diagnostics.as_ref(), ); // We also have a `#[rustc_regions]` annotation that causes us to dump // information. - nll::dump_annotation(&infcx, body, ®ioncx, &opt_closure_req); + nll::dump_annotation( + &infcx, + body, + &opt_closure_req, + &universal_region_relations.universal_regions, + ); let movable_coroutine = body.coroutine.is_some() && tcx.coroutine_movability(def.to_def_id()) == hir::Movability::Movable; @@ -475,7 +503,7 @@ fn borrowck_check_region_constraints<'tcx>( access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), uninitialized_error_reported: Default::default(), - regioncx: ®ioncx, + scc_values: &scc_values, used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: &borrow_set, @@ -487,6 +515,12 @@ fn borrowck_check_region_constraints<'tcx>( move_errors: Vec::new(), diags_buffer, polonius_diagnostics: polonius_diagnostics.as_ref(), + definitions: &definitions, + universal_region_relations: &universal_region_relations, + outlives_constraints: &outlives_constraints, + constraint_graph: outlives_constraints.graph(definitions.len()), + liveness_constraints: &liveness_constraints, + universe_causes: &universe_causes, }; struct MoveVisitor<'a, 'b, 'infcx, 'tcx> { ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>, @@ -514,7 +548,7 @@ fn borrowck_check_region_constraints<'tcx>( access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), uninitialized_error_reported: Default::default(), - regioncx: ®ioncx, + scc_values: &scc_values, used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: &borrow_set, @@ -526,6 +560,12 @@ fn borrowck_check_region_constraints<'tcx>( diags_buffer, polonius_output: polonius_output.as_deref(), polonius_diagnostics: polonius_diagnostics.as_ref(), + definitions: &definitions, + universal_region_relations: &universal_region_relations, + outlives_constraints: &outlives_constraints, + constraint_graph: outlives_constraints.graph(definitions.len()), + liveness_constraints: &liveness_constraints, + universe_causes: &universe_causes, }; // Compute and report region errors, if any. @@ -535,7 +575,8 @@ fn borrowck_check_region_constraints<'tcx>( mbcx.report_region_errors(nll_errors); } - let flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx); + let flow_results = + get_flow_results(tcx, body, &move_data, &borrow_set, &liveness_constraints, &scc_values); visit_results( body, traversal::reverse_postorder(body).map(|(bb, _)| bb), @@ -581,10 +622,10 @@ fn borrowck_check_region_constraints<'tcx>( body: body_owned, promoted, borrow_set, - region_inference_context: regioncx, location_table: polonius_input.as_ref().map(|_| location_table), input_facts: polonius_input, output_facts: polonius_output, + inferred_regions: scc_values, }, ); } @@ -599,15 +640,13 @@ fn get_flow_results<'a, 'tcx>( body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, borrow_set: &'a BorrowSet<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + liveness_values: &'a LivenessValues, + scc_values: &InferredRegions<'tcx>, ) -> 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( - tcx, - body, - Some("borrowck"), - ); + let borrows = Borrows::new(tcx, body, liveness_values, scc_values, borrow_set) + .iterate_to_fixpoint(tcx, body, Some("borrowck")); let uninits = MaybeUninitializedPlaces::new(tcx, body, move_data).iterate_to_fixpoint( tcx, body, @@ -749,9 +788,10 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { /// If the function we're checking is a closure, then we'll need to report back the list of /// mutable upvars that have been used. This field keeps track of them. used_mut_upvars: SmallVec<[FieldIdx; 8]>, - /// Region inference context. This contains the results from region inference and lets us e.g. + + /// This contains the results from region inference and lets us e.g. /// find out which CFG points are contained in each borrow region. - regioncx: &'a RegionInferenceContext<'tcx>, + scc_values: &'a InferredRegions<'tcx>, /// The set of borrows extracted from the MIR borrow_set: &'a BorrowSet<'tcx>, @@ -776,6 +816,17 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { polonius_output: Option<&'a PoloniusOutput>, /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics. polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>, + + /// Region variable definitions. Where they come from, etc. + definitions: &'a IndexVec>, + + universal_region_relations: &'a Frozen>, + constraint_graph: NormalConstraintGraph, + outlives_constraints: &'a OutlivesConstraintSet<'tcx>, + liveness_constraints: &'a LivenessValues, + + /// Map universe indexes to information on why we created it. + universe_causes: &'a FxIndexMap>, } // Check that: @@ -2687,6 +2738,71 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span }) } } + + /// Like `universal_upper_bound`, but returns an approximation more suitable + /// for diagnostics. If `r` contains multiple disjoint universal regions + /// (e.g. 'a and 'b in `fn foo<'a, 'b> { ... }`, we pick the lower-numbered region. + /// This corresponds to picking named regions over unnamed regions + /// (e.g. picking early-bound regions over a closure late-bound region). + /// + /// This means that the returned value may not be a true upper bound, since + /// only 'static is known to outlive disjoint universal regions. + /// Therefore, this method should only be used in diagnostic code, + /// where displaying *some* named universal region is better than + /// falling back to 'static. + #[instrument(level = "debug", skip(self))] + pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid { + debug!("{}", self.scc_values.region_value_str(r)); + + // Find the smallest universal region that contains all other + // universal regions within `region`. + let mut lub = self.universal_regions().fr_fn_body; + let static_r = self.universal_regions().fr_static; + for ur in self.scc_values.universal_regions_outlived_by(r) { + let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur); + debug!(?ur, ?lub, ?new_lub); + // The upper bound of two non-static regions is static: this + // means we know nothing about the relationship between these + // two regions. Pick a 'better' one to use when constructing + // a diagnostic + if ur != static_r && lub != static_r && new_lub == static_r { + // Prefer the region with an `external_name` - this + // indicates that the region is early-bound, so working with + // it can produce a nicer error. + if self.definitions[ur].external_name.is_some() { + lub = ur; + } else if self.definitions[lub].external_name.is_some() { + // Leave lub unchanged + } else { + // If we get here, we don't have any reason to prefer + // one region over the other. Just pick the + // one with the lower index for now. + lub = std::cmp::min(ur, lub); + } + } else { + lub = new_lub; + } + } + + debug!(?r, ?lub); + + lub + } + + pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> { + &self.universal_region_relations.universal_regions + } + + fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { + // Query canonicalization can create local superuniverses (for example in + // `InferCtx::query_response_instantiation_guess`), but they don't have an associated + // `UniverseInfo` explaining why they were created. + // This can cause ICEs if these causes are accessed in diagnostics, for example in issue + // #114907 where this happens via liveness and dropck outlives results. + // Therefore, we return a default value in case that happens, which should at worst emit a + // suboptimal error, instead of the ICE. + self.universe_causes.get(&universe).cloned().unwrap_or_else(UniverseInfo::other) + } } /// The degree of overlap between 2 places for borrow-checking. diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 64e3b59acfff9..f4e42084f9c21 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -7,11 +7,12 @@ use std::str::FromStr; use polonius_engine::{Algorithm, AllFacts, Output}; use rustc_data_structures::frozen::Frozen; -use rustc_index::IndexSlice; +use rustc_data_structures::fx::FxIndexMap; +use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::pretty::PrettyPrintMirOptions; use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_session::config::MirIncludeSpans; @@ -19,14 +20,19 @@ use rustc_span::sym; use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; +use crate::constraints::OutlivesConstraintSet; use crate::consumers::RustcFacts; -use crate::diagnostics::RegionErrors; -use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints; +use crate::diagnostics::{RegionErrors, UniverseInfo}; +use crate::handle_placeholders::{ + LoweredConstraints, compute_sccs_applying_placeholder_outlives_constraints, +}; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, }; use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext}; -use crate::region_infer::RegionInferenceContext; +use crate::region_infer::graphviz::{dump_graphviz_raw_constraints, dump_graphviz_scc_constraints}; +use crate::region_infer::values::LivenessValues; +use crate::region_infer::{self, InferredRegions, RegionDefinition, RegionInferenceContext}; use crate::type_check::MirTypeckRegionConstraints; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; @@ -38,11 +44,15 @@ use crate::{ /// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any /// closure requirements to propagate, and any generated errors. pub(crate) struct NllOutput<'tcx> { - pub regioncx: RegionInferenceContext<'tcx>, + pub scc_values: InferredRegions<'tcx>, pub polonius_input: Option>, pub polonius_output: Option>, pub opt_closure_req: Option>, pub nll_errors: RegionErrors<'tcx>, + pub(crate) definitions: Frozen>>, + pub(crate) liveness_constraints: LivenessValues, + pub(crate) outlives_constraints: Frozen>, + pub(crate) universe_causes: FxIndexMap>, /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g. /// localized typeck and liveness constraints. @@ -91,19 +101,34 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( ) -> Option> { // FIXME(#146079): we shouldn't have to clone all this stuff here. // Computing the region graph should take at least some of it by reference/`Rc`. - let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( + let LoweredConstraints { + constraint_sccs, + definitions, + scc_annotations, + outlives_constraints, + type_tests, + mut liveness_constraints, + universe_causes: _, + placeholder_indices, + } = compute_sccs_applying_placeholder_outlives_constraints( constraints.clone(), &universal_region_relations, infcx, ); - let mut regioncx = RegionInferenceContext::new( + + let regioncx = RegionInferenceContext::new( &infcx, - lowered_constraints, - universal_region_relations.clone(), - location_map, + constraint_sccs, + &definitions, + scc_annotations, + &outlives_constraints, + type_tests, + &mut liveness_constraints, + universal_region_relations, ); - let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, body, None); + let (closure_region_requirements, _nll_errors, _scc_values) = + regioncx.solve(infcx, body, None, location_map, placeholder_indices); closure_region_requirements } @@ -118,7 +143,7 @@ pub(crate) fn compute_regions<'tcx>( move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, location_map: Rc, - universal_region_relations: Frozen>, + universal_region_relations: &Frozen>, constraints: MirTypeckRegionConstraints<'tcx>, mut polonius_facts: Option>, polonius_context: Option, @@ -144,11 +169,26 @@ pub(crate) fn compute_regions<'tcx>( &lowered_constraints, ); + let LoweredConstraints { + constraint_sccs, + definitions, + scc_annotations, + outlives_constraints, + type_tests, + mut liveness_constraints, + universe_causes, + placeholder_indices, + } = lowered_constraints; + let mut regioncx = RegionInferenceContext::new( - infcx, - lowered_constraints, + &infcx, + constraint_sccs, + &definitions, + scc_annotations, + &outlives_constraints, + type_tests, + &mut liveness_constraints, universal_region_relations, - location_map, ); // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints @@ -179,16 +219,20 @@ pub(crate) fn compute_regions<'tcx>( }); // Solve the region constraints. - let (closure_region_requirements, nll_errors) = - regioncx.solve(infcx, body, polonius_output.clone()); + let (closure_region_requirements, nll_errors, scc_values) = + regioncx.solve(infcx, body, polonius_output.clone(), location_map, placeholder_indices); NllOutput { - regioncx, polonius_input: polonius_facts.map(Box::new), polonius_output, opt_closure_req: closure_region_requirements, nll_errors, polonius_diagnostics, + scc_values, + definitions, + liveness_constraints, + outlives_constraints, + universe_causes, } } @@ -204,9 +248,13 @@ pub(crate) fn compute_regions<'tcx>( pub(super) fn dump_nll_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + scc_values: &InferredRegions<'tcx>, closure_region_requirements: &Option>, borrow_set: &BorrowSet<'tcx>, + region_definitions: &IndexVec>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + outlives_constraints: &OutlivesConstraintSet<'tcx>, + liveness_constraints: &LivenessValues, ) { let tcx = infcx.tcx; let Some(dumper) = MirDumper::new(tcx, "nll", body) else { return }; @@ -222,7 +270,18 @@ pub(super) fn dump_nll_mir<'tcx>( }; let extra_data = &|pass_where, out: &mut dyn std::io::Write| { - emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out) + emit_nll_mir( + tcx, + scc_values, + closure_region_requirements, + borrow_set, + region_definitions, + outlives_constraints, + universal_region_relations, + liveness_constraints, + pass_where, + out, + ) }; let dumper = dumper.set_extra_data(extra_data).set_options(options); @@ -232,29 +291,40 @@ pub(super) fn dump_nll_mir<'tcx>( // Also dump the region constraint graph as a graphviz file. let _: io::Result<()> = try { let mut file = dumper.create_dump_file("regioncx.all.dot", body)?; - regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?; + dump_graphviz_raw_constraints(tcx, region_definitions, outlives_constraints, &mut file)?; }; // Also dump the region constraint SCC graph as a graphviz file. let _: io::Result<()> = try { let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?; - regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?; + dump_graphviz_scc_constraints(tcx, region_definitions, &scc_values.sccs, &mut file)?; }; } /// Produces the actual NLL MIR sections to emit during the dumping process. pub(crate) fn emit_nll_mir<'tcx>( tcx: TyCtxt<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + scc_values: &InferredRegions<'tcx>, closure_region_requirements: &Option>, borrow_set: &BorrowSet<'tcx>, + region_definitions: &IndexVec>, + outlives_constraints: &OutlivesConstraintSet<'tcx>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + liveness_constraints: &LivenessValues, pass_where: PassWhere, out: &mut dyn io::Write, ) -> io::Result<()> { match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => { - regioncx.dump_mir(tcx, out)?; + region_infer::MirDumper { + tcx, + definitions: region_definitions, + universal_region_relations, + outlives_constraints, + liveness_constraints, + } + .dump_mir(scc_values, out)?; writeln!(out, "|")?; if let Some(closure_region_requirements) = closure_region_requirements { @@ -290,8 +360,8 @@ pub(crate) fn emit_nll_mir<'tcx>( pub(super) fn dump_annotation<'tcx, 'infcx>( infcx: &'infcx BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, + universal_regions: &UniversalRegions<'tcx>, ) { let tcx = infcx.tcx; let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); @@ -310,7 +380,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>( let err = if let Some(closure_region_requirements) = closure_region_requirements { let mut err = infcx.dcx().struct_span_note(def_span, "external requirements"); - regioncx.annotate(tcx, &mut err); + universal_regions.annotate(tcx, &mut err); err.note(format!( "number of external vids: {}", @@ -328,7 +398,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>( err } else { let mut err = infcx.dcx().struct_span_note(def_span, "no external requirements"); - regioncx.annotate(tcx, &mut err); + universal_regions.annotate(tcx, &mut err); err }; diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 62f9ae173474d..91d26fe7e5df3 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -9,21 +9,27 @@ use rustc_mir_dataflow::points::PointIndex; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; -use crate::constraints::OutlivesConstraint; +use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; use crate::polonius::{ LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext, }; use crate::region_infer::values::LivenessValues; +use crate::region_infer::{InferredRegions, RegionDefinition}; use crate::type_check::Locations; -use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext}; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::{BorrowckInferCtxt, ClosureRegionRequirements}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. pub(crate) fn dump_polonius_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + scc_values: &InferredRegions<'tcx>, closure_region_requirements: &Option>, borrow_set: &BorrowSet<'tcx>, + region_definitions: &IndexVec>, + liveness_constraints: &LivenessValues, + outlives_constraints: &OutlivesConstraintSet<'tcx>, + universal_region_relations: &UniversalRegionRelations<'tcx>, polonius_diagnostics: Option<&PoloniusDiagnosticsContext>, ) { let tcx = infcx.tcx; @@ -39,10 +45,14 @@ pub(crate) fn dump_polonius_mir<'tcx>( let extra_data = &|pass_where, out: &mut dyn io::Write| { emit_polonius_mir( tcx, - regioncx, + scc_values, closure_region_requirements, borrow_set, &polonius_diagnostics.localized_outlives_constraints, + ®ion_definitions, + &outlives_constraints, + &universal_region_relations, + liveness_constraints, pass_where, out, ) @@ -63,7 +73,10 @@ pub(crate) fn dump_polonius_mir<'tcx>( emit_polonius_dump( &dumper, body, - regioncx, + region_definitions, + liveness_constraints, + outlives_constraints, + scc_values, borrow_set, &polonius_diagnostics.localized_outlives_constraints, &mut file, @@ -80,7 +93,10 @@ pub(crate) fn dump_polonius_mir<'tcx>( fn emit_polonius_dump<'tcx>( dumper: &MirDumper<'_, '_, 'tcx>, body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + region_definitions: &IndexVec>, + liveness_constraints: &LivenessValues, + outlives_constraints: &OutlivesConstraintSet<'tcx>, + scc_values: &InferredRegions<'tcx>, borrow_set: &BorrowSet<'tcx>, localized_outlives_constraints: &LocalizedOutlivesConstraintSet, out: &mut dyn io::Write, @@ -105,7 +121,7 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "
")?;
     let edge_count = emit_mermaid_constraint_graph(
         borrow_set,
-        regioncx.liveness_constraints(),
+        liveness_constraints,
         &localized_outlives_constraints,
         out,
     )?;
@@ -124,7 +140,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "
")?; writeln!(out, "NLL regions")?; writeln!(out, "
")?;
-    emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?;
+    emit_mermaid_nll_regions(dumper.tcx(), region_definitions, outlives_constraints, out)?;
     writeln!(out, "
")?; writeln!(out, "
")?; @@ -132,7 +148,7 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "
")?; writeln!(out, "NLL SCCs")?; writeln!(out, "
")?;
-    emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?;
+    emit_mermaid_nll_sccs(dumper.tcx(), region_definitions, scc_values, out)?;
     writeln!(out, "
")?; writeln!(out, "
")?; @@ -190,25 +206,31 @@ fn emit_html_mir<'tcx>( /// Produces the actual NLL + Polonius MIR sections to emit during the dumping process. fn emit_polonius_mir<'tcx>( tcx: TyCtxt<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + scc_values: &InferredRegions<'tcx>, closure_region_requirements: &Option>, borrow_set: &BorrowSet<'tcx>, localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + region_definitions: &IndexVec>, + outlives_constraints: &OutlivesConstraintSet<'tcx>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + liveness: &LivenessValues, pass_where: PassWhere, out: &mut dyn io::Write, ) -> io::Result<()> { // Emit the regular NLL front-matter crate::nll::emit_nll_mir( tcx, - regioncx, + scc_values, closure_region_requirements, borrow_set, + region_definitions, + outlives_constraints, + universal_region_relations, + liveness, pass_where, out, )?; - let liveness = regioncx.liveness_constraints(); - // Add localized outlives constraints match pass_where { PassWhere::BeforeCFG => { @@ -286,10 +308,10 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> fn render_region<'tcx>( tcx: TyCtxt<'tcx>, region: RegionVid, - regioncx: &RegionInferenceContext<'tcx>, + region_definitions: &IndexVec>, out: &mut dyn io::Write, ) -> io::Result<()> { - let def = regioncx.region_definition(region); + let def = ®ion_definitions[region]; let universe = def.universe; write!(out, "'{}", region.as_usize())?; @@ -306,21 +328,23 @@ fn render_region<'tcx>( /// to the graphviz version. fn emit_mermaid_nll_regions<'tcx>( tcx: TyCtxt<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + region_definitions: &IndexVec>, + outlives_constraints: &OutlivesConstraintSet<'tcx>, out: &mut dyn io::Write, ) -> io::Result<()> { // The mermaid chart type: a top-down flowchart. writeln!(out, "flowchart TD")?; // Emit the region nodes. - for region in regioncx.definitions.indices() { + for region in region_definitions.indices() { write!(out, "{}[\"", region.as_usize())?; - render_region(tcx, region, regioncx, out)?; + render_region(tcx, region, region_definitions, out)?; writeln!(out, "\"]")?; } // Get a set of edges to check for the reverse edge being present. - let edges: FxHashSet<_> = regioncx.outlives_constraints().map(|c| (c.sup, c.sub)).collect(); + let edges: FxHashSet<_> = + outlives_constraints.outlives().iter().map(|c| (c.sup, c.sub)).collect(); // Order (and deduplicate) edges for traversal, to display them in a generally increasing order. let constraint_key = |c: &OutlivesConstraint<'_>| { @@ -328,7 +352,7 @@ fn emit_mermaid_nll_regions<'tcx>( let max = c.sup.max(c.sub); (min, max) }; - let mut ordered_edges: Vec<_> = regioncx.outlives_constraints().collect(); + let mut ordered_edges: Vec<_> = outlives_constraints.outlives().iter().collect(); ordered_edges.sort_by_key(|c| constraint_key(c)); ordered_edges.dedup_by_key(|c| constraint_key(c)); @@ -358,7 +382,8 @@ fn emit_mermaid_nll_regions<'tcx>( /// to the graphviz version. fn emit_mermaid_nll_sccs<'tcx>( tcx: TyCtxt<'tcx>, - regioncx: &RegionInferenceContext<'tcx>, + region_definitions: &IndexVec>, + scc_values: &InferredRegions<'tcx>, out: &mut dyn io::Write, ) -> io::Result<()> { // The mermaid chart type: a top-down flowchart. @@ -366,16 +391,16 @@ fn emit_mermaid_nll_sccs<'tcx>( // Gather and emit the SCC nodes. let mut nodes_per_scc: IndexVec<_, _> = - regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect(); - for region in regioncx.definitions.indices() { - let scc = regioncx.constraint_sccs().scc(region); + scc_values.sccs.all_sccs().map(|_| Vec::new()).collect(); + for region in region_definitions.indices() { + let scc = scc_values.scc(region); nodes_per_scc[scc].push(region); } for (scc, regions) in nodes_per_scc.iter_enumerated() { // The node label: the regions contained in the SCC. write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?; for (idx, ®ion) in regions.iter().enumerate() { - render_region(tcx, region, regioncx, out)?; + render_region(tcx, region, region_definitions, out)?; if idx < regions.len() - 1 { write!(out, ",")?; } @@ -384,8 +409,8 @@ fn emit_mermaid_nll_sccs<'tcx>( } // Emit the edges between SCCs. - let edges = regioncx.constraint_sccs().all_sccs().flat_map(|source| { - regioncx.constraint_sccs().successors(source).iter().map(move |&target| (source, target)) + let edges = scc_values.sccs.all_sccs().flat_map(|source| { + scc_values.sccs.successors(source).iter().map(move |&target| (source, target)) }); for (source, target) in edges { writeln!(out, "{} --> {}", source.as_usize(), target.as_usize())?; diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index a9092b1981e1d..0ef1b29331d6d 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -153,7 +153,7 @@ impl PoloniusContext { pub(crate) fn compute_loan_liveness<'tcx>( self, tcx: TyCtxt<'tcx>, - regioncx: &mut RegionInferenceContext<'tcx>, + regioncx: &mut RegionInferenceContext<'_, 'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, ) -> PoloniusDiagnosticsContext { diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index 68f822aac403a..0437995ae2b8f 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -5,20 +5,38 @@ use std::io::{self, Write}; +use rustc_index::IndexVec; use rustc_infer::infer::NllRegionVariableOrigin; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{RegionVid, TyCtxt}; -use super::{OutlivesConstraint, RegionInferenceContext}; +use super::OutlivesConstraint; +use crate::constraints::OutlivesConstraintSet; +use crate::region_infer::values::LivenessValues; +use crate::region_infer::{InferredRegions, RegionDefinition}; use crate::type_check::Locations; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::universal_regions::UniversalRegions; // Room for "'_#NNNNr" before things get misaligned. // Easy enough to fix if this ever doesn't seem like // enough. const REGION_WIDTH: usize = 8; -impl<'tcx> RegionInferenceContext<'tcx> { +pub(crate) struct MirDumper<'a, 'tcx> { + pub(crate) tcx: TyCtxt<'tcx>, + pub(crate) definitions: &'a IndexVec>, + pub(crate) universal_region_relations: &'a UniversalRegionRelations<'tcx>, + pub(crate) outlives_constraints: &'a OutlivesConstraintSet<'tcx>, + pub(crate) liveness_constraints: &'a LivenessValues, +} + +impl<'a, 'tcx> MirDumper<'a, 'tcx> { /// Write out our state into the `.mir` files. - pub(crate) fn dump_mir(&self, tcx: TyCtxt<'tcx>, out: &mut dyn Write) -> io::Result<()> { + pub(crate) fn dump_mir( + &self, + scc_values: &InferredRegions<'tcx>, + out: &mut dyn Write, + ) -> io::Result<()> { writeln!(out, "| Free Region Mapping")?; for region in self.regions() { @@ -46,14 +64,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { "| {r:rw$?} | {ui:4?} | {v}", r = region, rw = REGION_WIDTH, - ui = self.max_nameable_universe(self.constraint_sccs.scc(region)), - v = self.region_value_str(region), + ui = scc_values.max_nameable_universe(region), + v = scc_values.region_value_str(region), )?; } writeln!(out, "|")?; writeln!(out, "| Inference Constraints")?; - self.for_each_constraint(tcx, &mut |msg| writeln!(out, "| {msg}"))?; + self.for_each_constraint(self.tcx, &mut |msg| writeln!(out, "| {msg}"))?; Ok(()) } @@ -74,7 +92,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - let mut constraints: Vec<_> = self.constraints.outlives().iter().collect(); + let mut constraints: Vec<_> = self.outlives_constraints.outlives().iter().collect(); constraints.sort_by_key(|c| (c.sup, c.sub)); for constraint in &constraints { let OutlivesConstraint { sup, sub, locations, category, span, .. } = constraint; @@ -89,4 +107,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { Ok(()) } + fn universal_regions(&self) -> &UniversalRegions<'tcx> { + &self.universal_region_relations.universal_regions + } + + /// Returns an iterator over all the region indices. + pub(crate) fn regions(&self) -> impl Iterator + 'tcx { + self.definitions.indices() + } } diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index 526e1850c2ef9..b226e513e7397 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -33,19 +33,19 @@ fn render_universe(u: UniverseIndex) -> String { fn render_region_vid<'tcx>( tcx: TyCtxt<'tcx>, rvid: RegionVid, - regioncx: &RegionInferenceContext<'tcx>, + region_definitions: &IndexVec>, ) -> String { - let universe_str = render_universe(regioncx.region_definition(rvid).universe); + let universe_str = render_universe(region_definitions[rvid].universe); let external_name_str = if let Some(external_name) = - regioncx.region_definition(rvid).external_name.and_then(|e| e.get_name(tcx)) + region_definitions[rvid].external_name.and_then(|e| e.get_name(tcx)) { format!(" ({external_name})") } else { "".to_string() }; - let extra_info = match regioncx.region_definition(rvid).origin { + let extra_info = match region_definitions[rvid].origin { NllRegionVariableOrigin::FreeRegion => "".to_string(), NllRegionVariableOrigin::Placeholder(p) => match p.bound.kind { ty::BoundRegionKind::Named(def_id) => { @@ -63,37 +63,38 @@ fn render_region_vid<'tcx>( format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid) } -impl<'tcx> RegionInferenceContext<'tcx> { - /// Write out the region constraint graph. - pub(crate) fn dump_graphviz_raw_constraints( - &self, - tcx: TyCtxt<'tcx>, - mut w: &mut dyn Write, - ) -> io::Result<()> { - dot::render(&RawConstraints { tcx, regioncx: self }, &mut w) - } - - /// Write out the region constraint SCC graph. - pub(crate) fn dump_graphviz_scc_constraints( - &self, - tcx: TyCtxt<'tcx>, - mut w: &mut dyn Write, - ) -> io::Result<()> { - let mut nodes_per_scc: IndexVec = - self.constraint_sccs.all_sccs().map(|_| Vec::new()).collect(); - - for region in self.definitions.indices() { - let scc = self.constraint_sccs.scc(region); - nodes_per_scc[scc].push(region); - } +/// Write out the region constraint graph. +pub(crate) fn dump_graphviz_raw_constraints<'a, 'tcx>( + tcx: TyCtxt<'tcx>, + region_definitions: &'a IndexVec>, + outlives_constraints: &'a OutlivesConstraintSet<'tcx>, + mut w: &mut dyn Write, +) -> io::Result<()> { + dot::render(&RawConstraints { tcx, region_definitions, outlives_constraints }, &mut w) +} - dot::render(&SccConstraints { tcx, regioncx: self, nodes_per_scc }, &mut w) +/// Write out the region constraint SCC graph. +pub(crate) fn dump_graphviz_scc_constraints<'a, 'tcx>( + tcx: TyCtxt<'tcx>, + region_definitions: &'a IndexVec>, + constraint_sccs: &'a ConstraintSccs, + mut w: &mut dyn Write, +) -> io::Result<()> { + let mut nodes_per_scc: IndexVec = + constraint_sccs.all_sccs().map(|_| Vec::new()).collect(); + + for region in region_definitions.indices() { + let scc = constraint_sccs.scc(region); + nodes_per_scc[scc].push(region); } + + dot::render(&SccConstraints { tcx, region_definitions, constraint_sccs, nodes_per_scc }, &mut w) } struct RawConstraints<'a, 'tcx> { tcx: TyCtxt<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, + region_definitions: &'a IndexVec>, + outlives_constraints: &'a OutlivesConstraintSet<'tcx>, } impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> { @@ -110,7 +111,7 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> { Some(dot::LabelText::LabelStr(Cow::Borrowed("box"))) } fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> { - dot::LabelText::LabelStr(render_region_vid(self.tcx, *n, self.regioncx).into()) + dot::LabelText::LabelStr(render_region_vid(self.tcx, *n, self.region_definitions).into()) } fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> { dot::LabelText::LabelStr(render_outlives_constraint(e).into()) @@ -122,11 +123,11 @@ impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> { type Edge = OutlivesConstraint<'tcx>; fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> { - let vids: Vec = self.regioncx.definitions.indices().collect(); + let vids: Vec = self.region_definitions.indices().collect(); vids.into() } fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint<'tcx>> { - (&self.regioncx.constraints.outlives().raw[..]).into() + (&self.outlives_constraints.outlives().raw[..]).into() } // Render `a: b` as `a -> b`, indicating the flow @@ -143,8 +144,9 @@ impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> { struct SccConstraints<'a, 'tcx> { tcx: TyCtxt<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, nodes_per_scc: IndexVec>, + region_definitions: &'a IndexVec>, + constraint_sccs: &'a ConstraintSccs, } impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> { @@ -163,7 +165,7 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> { fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> { let nodes_str = self.nodes_per_scc[*n] .iter() - .map(|n| render_region_vid(self.tcx, *n, self.regioncx)) + .map(|n| render_region_vid(self.tcx, *n, self.region_definitions)) .join(", "); dot::LabelText::LabelStr(format!("SCC({n}) = {{{nodes_str}}}", n = n.as_usize()).into()) } @@ -174,20 +176,15 @@ impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for SccConstraints<'a, 'tcx> { type Edge = (ConstraintSccIndex, ConstraintSccIndex); fn nodes(&'this self) -> dot::Nodes<'this, ConstraintSccIndex> { - let vids: Vec = self.regioncx.constraint_sccs.all_sccs().collect(); + let vids: Vec = self.constraint_sccs.all_sccs().collect(); vids.into() } fn edges(&'this self) -> dot::Edges<'this, (ConstraintSccIndex, ConstraintSccIndex)> { let edges: Vec<_> = self - .regioncx .constraint_sccs .all_sccs() .flat_map(|scc_a| { - self.regioncx - .constraint_sccs - .successors(scc_a) - .iter() - .map(move |&scc_b| (scc_a, scc_b)) + self.constraint_sccs.successors(scc_a).iter().map(move |&scc_b| (scc_a, scc_b)) }) .collect(); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 6ed70b39c5b7f..adcfad48a9966 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -4,7 +4,6 @@ use std::rc::Rc; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::scc::Sccs; -use rustc_errors::Diag; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_index::IndexVec; use rustc_infer::infer::outlives::test_type_match; @@ -25,11 +24,13 @@ use tracing::{Level, debug, enabled, instrument, trace}; use crate::constraints::graph::NormalConstraintGraph; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; -use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; -use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; +use crate::diagnostics::{RegionErrorKind, RegionErrors}; +use crate::handle_placeholders::RegionTracker; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; -use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; +use crate::region_infer::values::{ + LivenessValues, PlaceholderIndices, RegionElement, RegionValues, ToElementIndex, +}; use crate::type_check::Locations; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; @@ -39,11 +40,12 @@ use crate::{ }; mod dump_mir; -mod graphviz; +pub(crate) mod graphviz; pub(crate) mod opaque_types; mod reverse_sccs; pub(crate) mod values; +pub(crate) use dump_mir::MirDumper; /// The representative region variable for an SCC, tagged by its origin. /// We prefer placeholders over existentially quantified variables, otherwise @@ -76,21 +78,97 @@ impl Representative { pub(crate) type ConstraintSccs = Sccs; -pub struct RegionInferenceContext<'tcx> { +pub struct InferredRegions<'tcx> { + pub(crate) scc_values: RegionValues<'tcx, ConstraintSccIndex>, + pub(crate) sccs: ConstraintSccs, + annotations: IndexVec, +} + +impl<'tcx> InferredRegions<'tcx> { + /// Tries to find the terminator of the loop in which the region 'r' resides. + /// Returns the location of the terminator if found. + pub(crate) fn find_loop_terminator_location( + &self, + r: RegionVid, + body: &Body<'_>, + ) -> Option { + let locations = self.scc_values.locations_outlived_by(self.scc(r)); + for location in locations { + let bb = &body[location.block]; + if let Some(terminator) = &bb.terminator + // terminator of a loop should be TerminatorKind::FalseUnwind + && let TerminatorKind::FalseUnwind { .. } = terminator.kind + { + return Some(location); + } + } + None + } + + pub(crate) fn scc(&self, r: RegionVid) -> ConstraintSccIndex { + self.sccs.scc(r) + } + + /// Returns the lowest statement index in `start..=end` which is not contained by `r`. + pub(crate) fn first_non_contained_inclusive( + &self, + r: RegionVid, + block: BasicBlock, + start: usize, + end: usize, + ) -> Option { + self.scc_values.first_non_contained_inclusive(self.scc(r), block, start, end) + } + + /// Returns `true` if the region `r` contains the point `p`. + pub(crate) fn region_contains(&self, r: RegionVid, p: impl ToElementIndex<'tcx>) -> bool { + self.scc_values.contains(self.scc(r), p) + } + + /// Returns access to the value of `r` for debugging purposes. + pub(crate) fn region_value_str(&self, r: RegionVid) -> String { + self.scc_values.region_value_str(self.scc(r)) + } + + pub(crate) fn placeholders_contained_in( + &self, + r: RegionVid, + ) -> impl Iterator> { + self.scc_values.placeholders_contained_in(self.scc(r)) + } + + /// Check if the SCC of `r` contains `upper`. + pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { + self.scc_values.contains(self.scc(r), upper) + } + + pub(crate) fn universal_regions_outlived_by( + &self, + r: RegionVid, + ) -> impl Iterator { + self.scc_values.universal_regions_outlived_by(self.scc(r)) + } + + fn max_nameable_universe(&self, vid: RegionVid) -> UniverseIndex { + self.annotations[self.scc(vid)].max_nameable_universe() + } +} + +pub struct RegionInferenceContext<'a, 'tcx> { /// Contains the definition for every region variable. Region /// variables are identified by their index (`RegionVid`). The /// definition contains information about where the region came /// from as well as its final inferred value. - pub(crate) definitions: Frozen>>, + pub(crate) definitions: &'a Frozen>>, /// The liveness constraints added to each region. For most /// regions, these start out empty and steadily grow, though for /// each universally quantified region R they start out containing /// the entire CFG and `end(R)`. - liveness_constraints: LivenessValues, + liveness_constraints: &'a mut LivenessValues, /// The outlives constraints computed by the type-check. - constraints: Frozen>, + constraints: &'a OutlivesConstraintSet<'tcx>, /// The constraint-set, but in graph form, making it easy to traverse /// the constraints adjacent to a particular region. Used to construct @@ -104,20 +182,12 @@ pub struct RegionInferenceContext<'tcx> { scc_annotations: IndexVec, - /// Map universe indexes to information on why we created it. - universe_causes: FxIndexMap>, - - /// The final inferred values of the region variables; we compute - /// one value per SCC. To get the value for any given *region*, - /// you first find which scc it is a part of. - scc_values: RegionValues<'tcx, ConstraintSccIndex>, - /// Type constraints that we check after solving. type_tests: Vec>, /// Information about how the universally quantified regions in /// scope on this function relate to one another. - universal_region_relations: Frozen>, + universal_region_relations: &'a Frozen>, } #[derive(Debug)] @@ -281,7 +351,7 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { debug!("SCC edges {:#?}", scc_node_to_edges); } -impl<'tcx> RegionInferenceContext<'tcx> { +impl<'a, 'tcx> RegionInferenceContext<'a, 'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N /// of those will be constant regions representing the free @@ -291,26 +361,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// of constraints produced by the MIR type check. pub(crate) fn new( infcx: &BorrowckInferCtxt<'tcx>, - lowered_constraints: LoweredConstraints<'tcx>, - universal_region_relations: Frozen>, - location_map: Rc, + constraint_sccs: Sccs, + definitions: &'a Frozen>>, + scc_annotations: IndexVec, + outlives_constraints: &'a OutlivesConstraintSet<'tcx>, + type_tests: Vec>, + liveness_constraints: &'a mut LivenessValues, + universal_region_relations: &'a Frozen>, ) -> Self { - let universal_regions = &universal_region_relations.universal_regions; - - let LoweredConstraints { - constraint_sccs, - definitions, - outlives_constraints, - scc_annotations, - type_tests, - liveness_constraints, - universe_causes, - placeholder_indices, - } = lowered_constraints; - debug!("universal_regions: {:#?}", universal_region_relations.universal_regions); debug!("outlives constraints: {:#?}", outlives_constraints); - debug!("placeholder_indices: {:#?}", placeholder_indices); debug!("type tests: {:#?}", type_tests); let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len())); @@ -318,118 +378,25 @@ impl<'tcx> RegionInferenceContext<'tcx> { if cfg!(debug_assertions) { sccs_info(infcx, &constraint_sccs); } - - let mut scc_values = - RegionValues::new(location_map, universal_regions.len(), placeholder_indices); - - for region in liveness_constraints.regions() { - let scc = constraint_sccs.scc(region); - scc_values.merge_liveness(scc, region, &liveness_constraints); + for variable in definitions.indices() { + if let NllRegionVariableOrigin::FreeRegion = definitions[variable].origin { + // Add all nodes in the CFG to liveness constraints + liveness_constraints.add_all_points(variable); + } } - let mut result = Self { + Self { definitions, liveness_constraints, constraints: outlives_constraints, constraint_graph, constraint_sccs, scc_annotations, - universe_causes, - scc_values, type_tests, universal_region_relations, - }; - - result.init_free_and_bound_regions(); - - result - } - - /// Initializes the region variables for each universally - /// quantified region (lifetime parameter). The first N variables - /// always correspond to the regions appearing in the function - /// signature (both named and anonymous) and where-clauses. This - /// function iterates over those regions and initializes them with - /// minimum values. - /// - /// For example: - /// ```ignore (illustrative) - /// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ } - /// ``` - /// would initialize two variables like so: - /// ```ignore (illustrative) - /// R0 = { CFG, R0 } // 'a - /// R1 = { CFG, R0, R1 } // 'b - /// ``` - /// Here, R0 represents `'a`, and it contains (a) the entire CFG - /// and (b) any universally quantified regions that it outlives, - /// which in this case is just itself. R1 (`'b`) in contrast also - /// outlives `'a` and hence contains R0 and R1. - /// - /// This bit of logic also handles invalid universe relations - /// for higher-kinded types. - /// - /// We Walk each SCC `A` and `B` such that `A: B` - /// and ensure that universe(A) can see universe(B). - /// - /// This serves to enforce the 'empty/placeholder' hierarchy - /// (described in more detail on `RegionKind`): - /// - /// ```ignore (illustrative) - /// static -----+ - /// | | - /// empty(U0) placeholder(U1) - /// | / - /// empty(U1) - /// ``` - /// - /// In particular, imagine we have variables R0 in U0 and R1 - /// created in U1, and constraints like this; - /// - /// ```ignore (illustrative) - /// R1: !1 // R1 outlives the placeholder in U1 - /// R1: R0 // R1 outlives R0 - /// ``` - /// - /// Here, we wish for R1 to be `'static`, because it - /// cannot outlive `placeholder(U1)` and `empty(U0)` any other way. - /// - /// Thanks to this loop, what happens is that the `R1: R0` - /// constraint has lowered the universe of `R1` to `U0`, which in turn - /// means that the `R1: !1` constraint here will cause - /// `R1` to become `'static`. - fn init_free_and_bound_regions(&mut self) { - for variable in self.definitions.indices() { - let scc = self.constraint_sccs.scc(variable); - - match self.definitions[variable].origin { - NllRegionVariableOrigin::FreeRegion => { - // For each free, universally quantified region X: - - // Add all nodes in the CFG to liveness constraints - self.liveness_constraints.add_all_points(variable); - self.scc_values.add_all_points(scc); - - // Add `end(X)` into the set for X. - self.scc_values.add_element(scc, variable); - } - - NllRegionVariableOrigin::Placeholder(placeholder) => { - self.scc_values.add_element(scc, placeholder); - } - - NllRegionVariableOrigin::Existential { .. } => { - // For existential, regions, nothing to do. - } - } } } - /// Returns an iterator over all the region indices. - pub(crate) fn regions(&self) -> impl Iterator + 'tcx { - self.definitions.indices() - } - /// Given a universal region in scope on the MIR, returns the /// corresponding index. /// @@ -445,59 +412,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.constraints.outlives().iter().copied() } - /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`. - pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diag<'_, ()>) { - self.universal_regions().annotate(tcx, err) - } - - /// Returns `true` if the region `r` contains the point `p`. - /// - /// Panics if called before `solve()` executes, - pub(crate) fn region_contains(&self, r: RegionVid, p: impl ToElementIndex<'tcx>) -> bool { - let scc = self.constraint_sccs.scc(r); - self.scc_values.contains(scc, p) - } - - /// Returns the lowest statement index in `start..=end` which is not contained by `r`. - /// - /// Panics if called before `solve()` executes. - pub(crate) fn first_non_contained_inclusive( - &self, - r: RegionVid, - block: BasicBlock, - start: usize, - end: usize, - ) -> Option { - let scc = self.constraint_sccs.scc(r); - self.scc_values.first_non_contained_inclusive(scc, block, start, end) - } - - /// Returns access to the value of `r` for debugging purposes. - pub(crate) fn region_value_str(&self, r: RegionVid) -> String { - let scc = self.constraint_sccs.scc(r); - self.scc_values.region_value_str(scc) - } - - pub(crate) fn placeholders_contained_in( - &self, - r: RegionVid, - ) -> impl Iterator> { - let scc = self.constraint_sccs.scc(r); - self.scc_values.placeholders_contained_in(scc) - } - /// Performs region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. - #[instrument(skip(self, infcx, body, polonius_output), level = "debug")] + #[instrument( + skip(self, infcx, body, polonius_output, location_map, placeholder_indices), + level = "debug" + )] pub(super) fn solve( - &mut self, + self, infcx: &InferCtxt<'tcx>, body: &Body<'tcx>, polonius_output: Option>, - ) -> (Option>, RegionErrors<'tcx>) { + location_map: Rc, + placeholder_indices: PlaceholderIndices<'tcx>, + ) -> (Option>, RegionErrors<'tcx>, InferredRegions<'tcx>) { let mir_def_id = body.source.def_id(); - self.propagate_constraints(); + let scc_values = self.compute_region_values(location_map, placeholder_indices); let mut errors_buffer = RegionErrors::new(infcx.tcx); @@ -508,7 +439,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { // eagerly. let mut outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); - self.check_type_tests(infcx, outlives_requirements.as_mut(), &mut errors_buffer); + self.check_type_tests( + &scc_values, + infcx, + outlives_requirements.as_mut(), + &mut errors_buffer, + ); debug!(?errors_buffer); debug!(?outlives_requirements); @@ -518,6 +454,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // constraints were too strong, and if so, emit or propagate those errors. if infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled() { self.check_polonius_subset_errors( + &scc_values, outlives_requirements.as_mut(), &mut errors_buffer, polonius_output @@ -525,20 +462,31 @@ impl<'tcx> RegionInferenceContext<'tcx> { .expect("Polonius output is unavailable despite `-Z polonius`"), ); } else { - self.check_universal_regions(outlives_requirements.as_mut(), &mut errors_buffer); + self.check_universal_regions( + &scc_values, + outlives_requirements.as_mut(), + &mut errors_buffer, + ); } debug!(?errors_buffer); let outlives_requirements = outlives_requirements.unwrap_or_default(); + let scc_values = InferredRegions { + scc_values, + sccs: self.constraint_sccs, + annotations: self.scc_annotations, + }; if outlives_requirements.is_empty() { - (None, errors_buffer) + (None, errors_buffer, scc_values) } else { - let num_external_vids = self.universal_regions().num_global_and_external_regions(); + let num_external_vids = + self.universal_region_relations.universal_regions.num_global_and_external_regions(); ( Some(ClosureRegionRequirements { num_external_vids, outlives_requirements }), errors_buffer, + scc_values, ) } } @@ -547,8 +495,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// for each region variable until all the constraints are /// satisfied. Note that some values may grow **too** large to be /// feasible, but we check this later. - #[instrument(skip(self), level = "debug")] - fn propagate_constraints(&mut self) { + #[instrument(skip(self, location_map, placeholder_indices), level = "debug")] + fn compute_region_values( + &self, + location_map: Rc, + placeholder_indices: PlaceholderIndices<'tcx>, + ) -> RegionValues<'tcx, ConstraintSccIndex> { debug!("constraints={:#?}", { let mut constraints: Vec<_> = self.outlives_constraints().collect(); constraints.sort_by_key(|c| (c.sup, c.sub)); @@ -558,6 +510,33 @@ impl<'tcx> RegionInferenceContext<'tcx> { .collect::>() }); + let mut scc_values = + RegionValues::new(location_map, self.universal_regions().len(), placeholder_indices); + + for region in self.liveness_constraints.regions() { + scc_values.merge_liveness(self.scc(region), region, &self.liveness_constraints); + } + + for variable in self.definitions.indices() { + match self.definitions[variable].origin { + NllRegionVariableOrigin::FreeRegion => { + // For each free, universally quantified region X: + scc_values.add_all_points(self.scc(variable)); + + // Add `end(X)` into the set for X. + scc_values.add_element(self.scc(variable), variable); + } + + NllRegionVariableOrigin::Placeholder(placeholder) => { + scc_values.add_element(self.scc(variable), placeholder); + } + + NllRegionVariableOrigin::Existential { .. } => { + // For existential, regions, nothing to do. + } + } + } + // To propagate constraints, we walk the DAG induced by the // SCC. For each SCC `A`, we visit its successors and compute // their values, then we union all those values to get our @@ -566,9 +545,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Walk each SCC `B` such that `A: B`... for &scc_b in self.constraint_sccs.successors(scc_a) { debug!(?scc_b); - self.scc_values.add_region(scc_a, scc_b); + scc_values.add_region(scc_a, scc_b); } } + scc_values } /// Returns `true` if all the placeholders in the value of `scc_b` are nameable @@ -588,6 +568,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 'a`. See `TypeTest` for more details. fn check_type_tests( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, infcx: &InferCtxt<'tcx>, mut propagated_outlives_requirements: Option<&mut Vec>>, errors_buffer: &mut RegionErrors<'tcx>, @@ -604,6 +585,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let generic_ty = type_test.generic_kind.to_ty(tcx); if self.eval_verify_bound( + scc_values, infcx, generic_ty, type_test.lower_bound, @@ -613,7 +595,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { } if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements - && self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) + && self.try_promote_type_test( + scc_values, + infcx, + type_test, + propagated_outlives_requirements, + ) { continue; } @@ -663,9 +650,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// The idea then is to lower the `T: 'X` constraint into multiple /// bounds -- e.g., if `'X` is the union of two free lifetimes, /// `'1` and `'2`, then we would create `T: '1` and `T: '2`. - #[instrument(level = "debug", skip(self, infcx, propagated_outlives_requirements))] + #[instrument(level = "debug", skip(self, infcx, propagated_outlives_requirements, scc_values))] fn try_promote_type_test( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, infcx: &InferCtxt<'tcx>, type_test: &TypeTest<'tcx>, propagated_outlives_requirements: &mut Vec>, @@ -674,7 +662,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let TypeTest { generic_kind, lower_bound, span: blame_span, verify_bound: _ } = *type_test; let generic_ty = generic_kind.to_ty(tcx); - let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else { + let Some(subject) = self.try_promote_type_test_subject(scc_values, infcx, generic_ty) + else { return false; }; @@ -691,7 +680,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // // It doesn't matter *what* universe because the promoted `T` will // always be in the root universe. - if let Some(p) = self.scc_values.placeholders_contained_in(r_scc).next() { + if let Some(p) = scc_values.placeholders_contained_in(r_scc).next() { debug!("encountered placeholder in higher universe: {:?}, requiring 'static", p); let static_r = self.universal_regions().fr_static; propagated_outlives_requirements.push(ClosureOutlivesRequirement { @@ -710,7 +699,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // universal region (it may be the same region) and add it to // `ClosureOutlivesRequirement`. let mut found_outlived_universal_region = false; - for ur in self.scc_values.universal_regions_outlived_by(r_scc) { + for ur in scc_values.universal_regions_outlived_by(r_scc) { found_outlived_universal_region = true; debug!("universal_region_outlived_by ur={:?}", ur); let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); @@ -746,9 +735,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// variables in the type `T` with an equal universal region from the /// closure signature. /// This is not always possible, so this is a fallible process. - #[instrument(level = "debug", skip(self, infcx), ret)] + #[instrument(level = "debug", skip(self, infcx, scc_values), ret)] fn try_promote_type_test_subject( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option> { @@ -763,10 +753,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // regions. We want to find if that set is *equivalent* to // any of the named regions found in the closure. // To do so, we simply check every candidate `u_r` for equality. - self.scc_values + scc_values .universal_regions_outlived_by(r_scc) .filter(|&u_r| !self.universal_regions().is_local_free_region(u_r)) - .find(|&u_r| self.eval_equal(u_r, r_vid)) + .find(|&u_r| self.eval_equal(scc_values, u_r, r_vid)) .map(|u_r| ty::Region::new_var(tcx, u_r)) // In case we could not find a named region to map to, // we will return `None` below. @@ -786,61 +776,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::bind(tcx, ty))) } - /// Like `universal_upper_bound`, but returns an approximation more suitable - /// for diagnostics. If `r` contains multiple disjoint universal regions - /// (e.g. 'a and 'b in `fn foo<'a, 'b> { ... }`, we pick the lower-numbered region. - /// This corresponds to picking named regions over unnamed regions - /// (e.g. picking early-bound regions over a closure late-bound region). - /// - /// This means that the returned value may not be a true upper bound, since - /// only 'static is known to outlive disjoint universal regions. - /// Therefore, this method should only be used in diagnostic code, - /// where displaying *some* named universal region is better than - /// falling back to 'static. - #[instrument(level = "debug", skip(self))] - pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid { - debug!("{}", self.region_value_str(r)); - - // Find the smallest universal region that contains all other - // universal regions within `region`. - let mut lub = self.universal_regions().fr_fn_body; - let r_scc = self.constraint_sccs.scc(r); - let static_r = self.universal_regions().fr_static; - for ur in self.scc_values.universal_regions_outlived_by(r_scc) { - let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur); - debug!(?ur, ?lub, ?new_lub); - // The upper bound of two non-static regions is static: this - // means we know nothing about the relationship between these - // two regions. Pick a 'better' one to use when constructing - // a diagnostic - if ur != static_r && lub != static_r && new_lub == static_r { - // Prefer the region with an `external_name` - this - // indicates that the region is early-bound, so working with - // it can produce a nicer error. - if self.region_definition(ur).external_name.is_some() { - lub = ur; - } else if self.region_definition(lub).external_name.is_some() { - // Leave lub unchanged - } else { - // If we get here, we don't have any reason to prefer - // one region over the other. Just pick the - // one with the lower index for now. - lub = std::cmp::min(ur, lub); - } - } else { - lub = new_lub; - } - } - - debug!(?r, ?lub); - - lub - } - /// Tests if `test` is true when applied to `lower_bound` at /// `point`. fn eval_verify_bound( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, infcx: &InferCtxt<'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, @@ -850,31 +790,32 @@ impl<'tcx> RegionInferenceContext<'tcx> { match verify_bound { VerifyBound::IfEq(verify_if_eq_b) => { - self.eval_if_eq(infcx, generic_ty, lower_bound, *verify_if_eq_b) + self.eval_if_eq(scc_values, infcx, generic_ty, lower_bound, *verify_if_eq_b) } VerifyBound::IsEmpty => { let lower_bound_scc = self.constraint_sccs.scc(lower_bound); - self.scc_values.elements_contained_in(lower_bound_scc).next().is_none() + scc_values.elements_contained_in(lower_bound_scc).next().is_none() } VerifyBound::OutlivedBy(r) => { let r_vid = self.to_region_vid(*r); - self.eval_outlives(r_vid, lower_bound) + self.eval_outlives(scc_values, r_vid, lower_bound) } VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { - self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) + self.eval_verify_bound(scc_values, infcx, generic_ty, lower_bound, verify_bound) }), VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| { - self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) + self.eval_verify_bound(scc_values, infcx, generic_ty, lower_bound, verify_bound) }), } } fn eval_if_eq( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, infcx: &InferCtxt<'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, @@ -885,7 +826,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { match test_type_match::extract_verify_if_eq(infcx.tcx, &verify_if_eq_b, generic_ty) { Some(r) => { let r_vid = self.to_region_vid(r); - self.eval_outlives(r_vid, lower_bound) + self.eval_outlives(scc_values, r_vid, lower_bound) } None => false, } @@ -937,29 +878,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Evaluate whether `sup_region == sub_region`. /// - /// Panics if called before `solve()` executes, // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. - pub fn eval_equal(&self, r1: RegionVid, r2: RegionVid) -> bool { - self.eval_outlives(r1, r2) && self.eval_outlives(r2, r1) + pub fn eval_equal( + &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, + r1: RegionVid, + r2: RegionVid, + ) -> bool { + self.eval_outlives(scc_values, r1, r2) && self.eval_outlives(scc_values, r2, r1) } /// Evaluate whether `sup_region: sub_region`. /// - /// Panics if called before `solve()` executes, // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. - #[instrument(skip(self), level = "debug", ret)] - pub fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { - debug!( - "sup_region's value = {:?} universal={:?}", - self.region_value_str(sup_region), - self.universal_regions().is_universal_region(sup_region), - ); - debug!( - "sub_region's value = {:?} universal={:?}", - self.region_value_str(sub_region), - self.universal_regions().is_universal_region(sub_region), - ); - + #[instrument(skip(self, scc_values), level = "debug", ret)] + pub fn eval_outlives( + &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, + sup_region: RegionVid, + sub_region: RegionVid, + ) -> bool { let sub_region_scc = self.constraint_sccs.scc(sub_region); let sup_region_scc = self.constraint_sccs.scc(sup_region); @@ -983,7 +921,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { by super `{sup_region_scc:?}`, promoting to static", ); - return self.eval_outlives(sup_region, fr_static); + return self.eval_outlives(scc_values, sup_region, fr_static); } // Both the `sub_region` and `sup_region` consist of the union @@ -993,8 +931,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // for each universal region R1 in the sub-region, there // exists some region R2 in the sup-region that outlives R1. let universal_outlives = - self.scc_values.universal_regions_outlived_by(sub_region_scc).all(|r1| { - self.scc_values + scc_values.universal_regions_outlived_by(sub_region_scc).all(|r1| { + scc_values .universal_regions_outlived_by(sup_region_scc) .any(|r2| self.universal_region_relations.outlives(r2, r1)) }); @@ -1015,7 +953,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("comparison between points in sup/sub"); - self.scc_values.contains_points(sup_region_scc, sub_region_scc) + scc_values.contains_points(sup_region_scc, sub_region_scc) } /// Once regions have been propagated, this method is used to see @@ -1037,6 +975,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// report them as errors. fn check_universal_regions( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, mut propagated_outlives_requirements: Option<&mut Vec>>, errors_buffer: &mut RegionErrors<'tcx>, ) { @@ -1048,6 +987,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // they did not grow too large, accumulating any requirements // for our caller into the `outlives_requirements` vector. self.check_universal_region( + scc_values, fr, &mut propagated_outlives_requirements, errors_buffer, @@ -1055,7 +995,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } NllRegionVariableOrigin::Placeholder(placeholder) => { - self.check_bound_universal_region(fr, placeholder, errors_buffer); + self.check_bound_universal_region(scc_values, fr, placeholder, errors_buffer); } NllRegionVariableOrigin::Existential { .. } => { @@ -1088,6 +1028,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// report them as errors. fn check_polonius_subset_errors( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, mut propagated_outlives_requirements: Option<&mut Vec>>, errors_buffer: &mut RegionErrors<'tcx>, polonius_output: &PoloniusOutput, @@ -1134,6 +1075,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); let propagated = self.try_propagate_universal_region_error( + scc_values, longer_fr.into(), shorter_fr.into(), &mut propagated_outlives_requirements, @@ -1157,7 +1099,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } NllRegionVariableOrigin::Placeholder(placeholder) => { - self.check_bound_universal_region(fr, placeholder, errors_buffer); + self.check_bound_universal_region(scc_values, fr, placeholder, errors_buffer); } NllRegionVariableOrigin::Existential { .. } => { @@ -1180,9 +1122,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Things that are to be propagated are accumulated into the /// `outlives_requirements` vector. - #[instrument(skip(self, propagated_outlives_requirements, errors_buffer), level = "debug")] + #[instrument( + skip(self, propagated_outlives_requirements, errors_buffer, scc_values), + level = "debug" + )] fn check_universal_region( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, longer_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec>>, errors_buffer: &mut RegionErrors<'tcx>, @@ -1202,6 +1148,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let representative = self.scc_representative(longer_fr_scc); if representative != longer_fr { if let RegionRelationCheckResult::Error = self.check_universal_region_relation( + scc_values, longer_fr, representative, propagated_outlives_requirements, @@ -1219,8 +1166,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Find every region `o` such that `fr: o` // (because `fr` includes `end(o)`). let mut error_reported = false; - for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) { + for shorter_fr in scc_values.universal_regions_outlived_by(longer_fr_scc) { if let RegionRelationCheckResult::Error = self.check_universal_region_relation( + scc_values, longer_fr, shorter_fr, propagated_outlives_requirements, @@ -1245,6 +1193,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// error. fn check_universal_region_relation( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, longer_fr: RegionVid, shorter_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec>>, @@ -1260,6 +1209,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Note: in this case, we use the unapproximated regions to report the // error. This gives better error messages in some cases. self.try_propagate_universal_region_error( + scc_values, longer_fr, shorter_fr, propagated_outlives_requirements, @@ -1271,6 +1221,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// creator. If we cannot, then the caller should report an error to the user. fn try_propagate_universal_region_error( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, longer_fr: RegionVid, shorter_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec>>, @@ -1290,6 +1241,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } let blame_constraint = self + .constraint_search() .best_blame_constraint(longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr) .0; @@ -1330,7 +1282,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let subset: Vec<_> = constraints .iter() .filter(|&&(fr_minus, shorter_fr_plus)| { - self.eval_outlives(fr_minus, shorter_fr_plus) + self.eval_outlives(scc_values, fr_minus, shorter_fr_plus) }) .copied() .collect(); @@ -1362,6 +1314,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn check_bound_universal_region( &self, + scc_values: &RegionValues<'tcx, ConstraintSccIndex>, longer_fr: RegionVid, placeholder: ty::PlaceholderRegion<'tcx>, errors_buffer: &mut RegionErrors<'tcx>, @@ -1374,8 +1327,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If we have some bound universal region `'a`, then the only // elements it can contain is itself -- we don't know anything // else about it! - if let Some(error_element) = self - .scc_values + if let Some(error_element) = scc_values .elements_contained_in(longer_fr_scc) .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) { @@ -1390,15 +1342,135 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - pub(crate) fn constraint_path_between_regions( + fn constraint_search(&'a self) -> ConstraintSearch<'a, 'tcx> { + ConstraintSearch { + definitions: &self.definitions, + fr_static: self.universal_regions().fr_static, + constraint_graph: &self.constraint_graph, + constraints: &self.constraints, + } + } + + fn constraint_path_to( &self, from_region: RegionVid, to_region: RegionVid, + follow_outlives_static: bool, ) -> Option>> { - if from_region == to_region { - bug!("Tried to find a path between {from_region:?} and itself!"); + self.constraint_search() + .constraint_path_to(from_region, |t| t == to_region, follow_outlives_static) + .map(|o| o.0) + } + + /// Get the region definition of `r`. + pub(crate) fn region_definition(&self, r: RegionVid) -> &RegionDefinition<'tcx> { + &self.definitions[r] + } + + pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> { + &self.universal_region_relations.universal_regions + } + + /// Access to the SCC constraint graph. + /// This can be used to quickly under-approximate the regions which are equal to each other + /// and their relative orderings. + // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. + pub fn constraint_sccs(&self) -> &ConstraintSccs { + &self.constraint_sccs + } + + /// Returns the representative `RegionVid` for a given SCC. + /// See `RegionTracker` for how a region variable ID is chosen. + /// + /// It is a hacky way to manage checking regions for equality, + /// since we can 'canonicalize' each region to the representative + /// of its SCC and be sure that -- if they have the same repr -- + /// they *must* be equal (though not having the same repr does not + /// mean they are unequal). + fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { + self.scc_annotations[scc].representative.rvid() + } + + pub(crate) fn liveness_constraints(&self) -> &LivenessValues { + &self.liveness_constraints + } + + /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active + /// loans dataflow computations. + pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { + self.liveness_constraints.record_live_loans(live_loans); + } + + /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing + /// region is contained within the type of a variable that is live at this point. + /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. + pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool { + let point = self.liveness_constraints.point_from_location(location); + self.liveness_constraints.is_loan_live_at(loan_idx, point) + } + + pub(crate) fn scc(&self, r: RegionVid) -> ConstraintSccIndex { + self.constraint_sccs.scc(r) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct BlameConstraint<'tcx> { + pub category: ConstraintCategory<'tcx>, + pub from_closure: bool, + pub cause: ObligationCause<'tcx>, + pub variance_info: ty::VarianceDiagInfo>, +} + +pub(crate) struct ConstraintSearch<'a, 'tcx> { + pub(crate) definitions: &'a IndexVec>, + pub(crate) fr_static: RegionVid, + pub(crate) constraint_graph: &'a NormalConstraintGraph, + pub(crate) constraints: &'a OutlivesConstraintSet<'tcx>, +} + +impl<'a, 'tcx> ConstraintSearch<'a, 'tcx> { + /// Get the region outlived by `longer_fr` and live at `element`. + pub(crate) fn region_from_element( + &self, + liveness_constraints: &LivenessValues, + longer_fr: RegionVid, + element: &RegionElement<'tcx>, + definitions: &IndexVec>, + ) -> RegionVid { + match *element { + RegionElement::Location(l) => { + self.find_sub_region_live_at(liveness_constraints, longer_fr, l) + } + RegionElement::RootUniversalRegion(r) => r, + RegionElement::PlaceholderRegion(error_placeholder) => definitions + .iter_enumerated() + .find_map(|(r, definition)| match definition.origin { + NllRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), + _ => None, + }) + .unwrap(), } - self.constraint_path_to(from_region, |to| to == to_region, true).map(|o| o.0) + } + + /// Finds some region R such that `fr1: R` and `R` is live at `location`. + #[instrument(skip(self, liveness_constraints), level = "trace", ret)] + pub(crate) fn find_sub_region_live_at( + &self, + liveness_constraints: &LivenessValues, + fr1: RegionVid, + location: Location, + ) -> RegionVid { + self.constraint_path_to( + fr1, + |r| { + trace!(?r, liveness_constraints=?liveness_constraints.pretty_print_live_points(r)); + liveness_constraints.is_live_at(r, location) + }, + true, + ) + .unwrap() + .1 } /// Walks the graph of constraints (where `'a: 'b` is considered @@ -1433,6 +1505,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) } + pub(crate) fn constraint_path_between_regions( + &self, + from_region: RegionVid, + to_region: RegionVid, + ) -> Option>> { + if from_region == to_region { + bug!("Tried to find a path between {from_region:?} and itself!"); + } + self.constraint_path_to(from_region, |t| t == to_region, true).map(|o| o.0) + } + /// The constraints we get from equating the hidden type of each use of an opaque /// with its final hidden type may end up getting preferred over other, potentially /// longer constraint paths. @@ -1452,10 +1535,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { target_test: impl Fn(RegionVid) -> bool, include_placeholder_static: bool, ) -> Option<(Vec>, RegionVid)> { - let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions); + let mut context = IndexVec::from_elem(Trace::NotVisited, self.definitions); context[from_region] = Trace::StartRegion; - - let fr_static = self.universal_regions().fr_static; + let fr_static = self.fr_static; // Use a deque so that we do a breadth-first search. We will // stop at the first match, which ought to be the shortest @@ -1464,12 +1546,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { deque.push_back(from_region); while let Some(r) = deque.pop_front() { - debug!( - "constraint_path_to: from_region={:?} r={:?} value={}", - from_region, - r, - self.region_value_str(r), - ); + debug!("constraint_path_to: from_region={:?} r={:?}", from_region, r,); // Check if we reached the region we were looking for. If so, // we can reconstruct the path that led to it and return it. @@ -1534,7 +1611,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { handle_trace(sub, Trace::FromStatic(sub)); } } else { - let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints); + let edges = self.constraint_graph.outgoing_edges_from_graph(r, self.constraints); // This loop can be hot. for constraint in edges { match constraint.category { @@ -1560,52 +1637,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { None } - /// Finds some region R such that `fr1: R` and `R` is live at `location`. - #[instrument(skip(self), level = "trace", ret)] - pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid { - trace!(scc = ?self.constraint_sccs.scc(fr1)); - trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1))); - self.constraint_path_to(fr1, |r| { - trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r)); - self.liveness_constraints.is_live_at(r, location) - }, true).unwrap().1 - } - - /// Get the region outlived by `longer_fr` and live at `element`. - pub(crate) fn region_from_element( - &self, - longer_fr: RegionVid, - element: &RegionElement<'tcx>, - ) -> RegionVid { - match *element { - RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), - RegionElement::RootUniversalRegion(r) => r, - RegionElement::PlaceholderRegion(error_placeholder) => self - .definitions - .iter_enumerated() - .find_map(|(r, definition)| match definition.origin { - NllRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), - _ => None, - }) - .unwrap(), - } - } - - /// Get the region definition of `r`. - pub(crate) fn region_definition(&self, r: RegionVid) -> &RegionDefinition<'tcx> { - &self.definitions[r] - } - - /// Check if the SCC of `r` contains `upper`. - pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { - let r_scc = self.constraint_sccs.scc(r); - self.scc_values.contains(r_scc, upper) - } - - pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> { - &self.universal_region_relations.universal_regions - } - /// Tries to find the best constraint to blame for the fact that /// `R: from_region`, where `R` is some region that meets /// `target_test`. This works by following the constraint graph, @@ -1621,7 +1652,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> (BlameConstraint<'tcx>, Vec>) { assert!(from_region != to_region, "Trying to blame a region for itself!"); - let path = self.constraint_path_between_regions(from_region, to_region).unwrap(); + let path = self.constraint_path_to(from_region, |t| t == to_region, true).unwrap().0; // If we are passing through a constraint added because we reached an unnameable placeholder `'unnameable`, // redirect search towards `'unnameable`. @@ -1639,23 +1670,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { { // We ignore the extra edges due to unnameable placeholders to get // an explanation that was present in the original constraint graph. - self.constraint_path_to(from_region, |r| r == unnameable, false).unwrap().0 + self.constraint_path_to(from_region, |t| t == unnameable, false).unwrap().0 } else { path }; - debug!( - "path={:#?}", - path.iter() - .map(|c| format!( - "{:?} ({:?}: {:?})", - c, - self.constraint_sccs.scc(c.sup), - self.constraint_sccs.scc(c.sub), - )) - .collect::>() - ); - // We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint. // Instead, we use it to produce an improved `ObligationCauseCode`. // FIXME - determine what we should do if we encounter multiple @@ -1752,7 +1771,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { is_raw_ptr_dyn_type_cast: _, unsize_to: Some(unsize_ty), is_implicit_coercion: true, - } if to_region == self.universal_regions().fr_static + } if to_region == self.fr_static // Mirror the note's condition, to minimize how often this diverts blame. && let ty::Adt(_, args) = unsize_ty.kind() && args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait())) @@ -1844,82 +1863,4 @@ impl<'tcx> RegionInferenceContext<'tcx> { }; (blame_constraint, path) } - - pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { - // Query canonicalization can create local superuniverses (for example in - // `InferCtx::query_response_instantiation_guess`), but they don't have an associated - // `UniverseInfo` explaining why they were created. - // This can cause ICEs if these causes are accessed in diagnostics, for example in issue - // #114907 where this happens via liveness and dropck outlives results. - // Therefore, we return a default value in case that happens, which should at worst emit a - // suboptimal error, instead of the ICE. - self.universe_causes.get(&universe).cloned().unwrap_or_else(UniverseInfo::other) - } - - /// Tries to find the terminator of the loop in which the region 'r' resides. - /// Returns the location of the terminator if found. - pub(crate) fn find_loop_terminator_location( - &self, - r: RegionVid, - body: &Body<'_>, - ) -> Option { - let scc = self.constraint_sccs.scc(r); - let locations = self.scc_values.locations_outlived_by(scc); - for location in locations { - let bb = &body[location.block]; - if let Some(terminator) = &bb.terminator - // terminator of a loop should be TerminatorKind::FalseUnwind - && let TerminatorKind::FalseUnwind { .. } = terminator.kind - { - return Some(location); - } - } - None - } - - /// Access to the SCC constraint graph. - /// This can be used to quickly under-approximate the regions which are equal to each other - /// and their relative orderings. - // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. - pub fn constraint_sccs(&self) -> &ConstraintSccs { - &self.constraint_sccs - } - - /// Returns the representative `RegionVid` for a given SCC. - /// See `RegionTracker` for how a region variable ID is chosen. - /// - /// It is a hacky way to manage checking regions for equality, - /// since we can 'canonicalize' each region to the representative - /// of its SCC and be sure that -- if they have the same repr -- - /// they *must* be equal (though not having the same repr does not - /// mean they are unequal). - fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { - self.scc_annotations[scc].representative.rvid() - } - - pub(crate) fn liveness_constraints(&self) -> &LivenessValues { - &self.liveness_constraints - } - - /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active - /// loans dataflow computations. - pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { - self.liveness_constraints.record_live_loans(live_loans); - } - - /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing - /// region is contained within the type of a variable that is live at this point. - /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. - pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool { - let point = self.liveness_constraints.point_from_location(location); - self.liveness_constraints.is_loan_live_at(loan_idx, point) - } -} - -#[derive(Clone, Debug)] -pub(crate) struct BlameConstraint<'tcx> { - pub category: ConstraintCategory<'tcx>, - pub from_closure: bool, - pub cause: ObligationCause<'tcx>, - pub variance_info: ty::VarianceDiagInfo>, } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 0c4a82f3d2f36..d12a74bd1f836 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -24,13 +24,12 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use tracing::{debug, instrument}; use super::reverse_sccs::ReverseSccGraph; -use crate::BorrowckInferCtxt; -use crate::consumers::RegionInferenceContext; use crate::session_diagnostics::LifetimeMismatchOpaqueParam; use crate::type_check::canonical::fully_perform_op_raw; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::{RegionClassification, UniversalRegions}; +use crate::{BorrowckInferCtxt, MirBorrowckCtxt}; mod member_constraints; mod region_ctxt; @@ -595,7 +594,7 @@ pub(crate) fn detect_opaque_types_added_while_handling_opaque_types<'tcx>( let _ = infcx.take_opaque_types(); } -impl<'tcx> RegionInferenceContext<'tcx> { +impl<'a, 'infcx, 'tcx> MirBorrowckCtxt<'a, 'infcx, 'tcx> { /// Map the regions in the type to named regions. This is similar to what /// `infer_opaque_types` does, but can infer any universal region, not only /// ones from the args for the opaque type. It also doesn't double check @@ -614,11 +613,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { { fold_regions(tcx, ty, |region, _| match region.kind() { ty::ReVar(vid) => { - let scc = self.constraint_sccs.scc(vid); - // Special handling of higher-ranked regions. - if !self.max_nameable_universe(scc).is_root() { - match self.scc_values.placeholders_contained_in(scc).enumerate().last() { + if !self.scc_values.max_nameable_universe(vid).is_root() { + match self.scc_values.placeholders_contained_in(vid).enumerate().last() { // If the region contains a single placeholder then they're equal. Some((0, placeholder)) => { return ty::Region::new_placeholder(tcx, placeholder); @@ -639,11 +636,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If there's >1 universal region, then we probably are dealing w/ an intersection // region which cannot be mapped back to a universal. // FIXME: We could probably compute the LUB if there is one. - let scc = self.constraint_sccs.scc(vid); let rev_scc_graph = - ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions()); + ReverseSccGraph::compute(&self.scc_values.sccs, self.universal_regions()); let upper_bounds: Vec<_> = rev_scc_graph - .upper_bounds(scc) + .upper_bounds(self.scc_values.scc(vid)) .filter_map(|vid| self.definitions[vid].external_name) .filter(|r| !r.is_static()) .collect();