From 9918693037dce8aa4bb6f08741b6812923486c18 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 19 Jun 2024 11:26:03 +0200 Subject: Merging upstream version 1.76.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_borrowck/src/borrow_set.rs | 6 +- .../rustc_borrowck/src/constraint_generation.rs | 248 ------------ compiler/rustc_borrowck/src/constraints/mod.rs | 1 + compiler/rustc_borrowck/src/dataflow.rs | 193 +++++---- .../src/diagnostics/bound_region_errors.rs | 52 ++- .../src/diagnostics/conflict_errors.rs | 111 +++--- .../src/diagnostics/explain_borrow.rs | 64 ++- compiler/rustc_borrowck/src/diagnostics/mod.rs | 187 +++++---- .../rustc_borrowck/src/diagnostics/move_errors.rs | 11 +- .../src/diagnostics/mutability_errors.rs | 162 ++++++-- .../src/diagnostics/outlives_suggestion.rs | 8 +- .../src/diagnostics/region_errors.rs | 41 +- .../rustc_borrowck/src/diagnostics/region_name.rs | 110 ++--- .../rustc_borrowck/src/diagnostics/var_name.rs | 9 +- compiler/rustc_borrowck/src/invalidation.rs | 441 --------------------- compiler/rustc_borrowck/src/lib.rs | 103 ++--- compiler/rustc_borrowck/src/location.rs | 1 + compiler/rustc_borrowck/src/nll.rs | 207 ++-------- compiler/rustc_borrowck/src/path_utils.rs | 5 +- .../src/polonius/loan_invalidations.rs | 424 ++++++++++++++++++++ compiler/rustc_borrowck/src/polonius/loan_kills.rs | 147 +++++++ compiler/rustc_borrowck/src/polonius/mod.rs | 188 +++++++++ .../rustc_borrowck/src/region_infer/dump_mir.rs | 2 +- .../rustc_borrowck/src/region_infer/graphviz.rs | 1 - compiler/rustc_borrowck/src/region_infer/mod.rs | 66 ++- .../src/region_infer/opaque_types.rs | 6 +- compiler/rustc_borrowck/src/region_infer/values.rs | 148 +++++-- compiler/rustc_borrowck/src/renumber.rs | 3 + .../src/type_check/free_region_relations.rs | 7 +- .../rustc_borrowck/src/type_check/input_output.rs | 10 +- .../src/type_check/liveness/local_use_map.rs | 2 +- .../rustc_borrowck/src/type_check/liveness/mod.rs | 88 +++- .../src/type_check/liveness/polonius.rs | 2 +- .../src/type_check/liveness/trace.rs | 80 +--- compiler/rustc_borrowck/src/type_check/mod.rs | 121 +++--- .../rustc_borrowck/src/type_check/relate_tys.rs | 4 +- compiler/rustc_borrowck/src/universal_regions.rs | 74 +--- compiler/rustc_borrowck/src/used_muts.rs | 2 +- 38 files changed, 1771 insertions(+), 1564 deletions(-) delete mode 100644 compiler/rustc_borrowck/src/constraint_generation.rs delete mode 100644 compiler/rustc_borrowck/src/invalidation.rs create mode 100644 compiler/rustc_borrowck/src/polonius/loan_invalidations.rs create mode 100644 compiler/rustc_borrowck/src/polonius/loan_kills.rs create mode 100644 compiler/rustc_borrowck/src/polonius/mod.rs (limited to 'compiler/rustc_borrowck/src') diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 5248a649c..948af0395 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -107,7 +107,7 @@ impl LocalsStateAtExit { LocalsStateAtExit::AllAreInvalidated } else { let mut has_storage_dead = HasStorageDead(BitSet::new_empty(body.local_decls.len())); - has_storage_dead.visit_body(&body); + has_storage_dead.visit_body(body); let mut has_storage_dead_or_moved = has_storage_dead.0; for move_out in &move_data.moves { if let Some(index) = move_data.base_local(move_out.path) { @@ -128,7 +128,7 @@ impl<'tcx> BorrowSet<'tcx> { ) -> Self { let mut visitor = GatherBorrows { tcx, - body: &body, + body: body, location_map: Default::default(), activation_map: Default::default(), local_map: Default::default(), @@ -140,7 +140,7 @@ impl<'tcx> BorrowSet<'tcx> { ), }; - for (block, block_data) in traversal::preorder(&body) { + for (block, block_data) in traversal::preorder(body) { visitor.visit_basic_block_data(block, block_data); } diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs deleted file mode 100644 index 1f642099f..000000000 --- a/compiler/rustc_borrowck/src/constraint_generation.rs +++ /dev/null @@ -1,248 +0,0 @@ -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] -use rustc_infer::infer::InferCtxt; -use rustc_middle::mir::visit::TyContext; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ - Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, SourceInfo, Statement, - StatementKind, Terminator, TerminatorKind, UserTypeProjection, -}; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; - -use crate::{ - borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, places_conflict, - region_infer::values::LivenessValues, -}; - -pub(super) fn generate_constraints<'tcx>( - infcx: &InferCtxt<'tcx>, - liveness_constraints: &mut LivenessValues, - all_facts: &mut Option, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, -) { - let mut cg = ConstraintGeneration { - borrow_set, - infcx, - liveness_constraints, - location_table, - all_facts, - body, - }; - - for (bb, data) in body.basic_blocks.iter_enumerated() { - cg.visit_basic_block_data(bb, data); - } -} - -/// 'cg = the duration of the constraint generation process itself. -struct ConstraintGeneration<'cg, 'tcx> { - infcx: &'cg InferCtxt<'tcx>, - all_facts: &'cg mut Option, - location_table: &'cg LocationTable, - liveness_constraints: &'cg mut LivenessValues, - borrow_set: &'cg BorrowSet<'tcx>, - body: &'cg Body<'tcx>, -} - -impl<'cg, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'tcx> { - /// We sometimes have `args` within an rvalue, or within a - /// call. Make them live at the location where they appear. - fn visit_args(&mut self, args: &GenericArgsRef<'tcx>, location: Location) { - self.add_regular_live_constraint(*args, location); - self.super_args(args); - } - - /// We sometimes have `region` within an rvalue, or within a - /// call. Make them live at the location where they appear. - fn visit_region(&mut self, region: ty::Region<'tcx>, location: Location) { - self.add_regular_live_constraint(region, location); - self.super_region(region); - } - - /// We sometimes have `ty` within an rvalue, or within a - /// call. Make them live at the location where they appear. - fn visit_ty(&mut self, ty: Ty<'tcx>, ty_context: TyContext) { - match ty_context { - TyContext::ReturnTy(SourceInfo { span, .. }) - | TyContext::YieldTy(SourceInfo { span, .. }) - | TyContext::UserTy(span) - | TyContext::LocalDecl { source_info: SourceInfo { span, .. }, .. } => { - span_bug!(span, "should not be visiting outside of the CFG: {:?}", ty_context); - } - TyContext::Location(location) => { - self.add_regular_live_constraint(ty, location); - } - } - - self.super_ty(ty); - } - - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { - if let Some(all_facts) = self.all_facts { - let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - all_facts.cfg_edge.push(( - self.location_table.start_index(location), - self.location_table.mid_index(location), - )); - - all_facts.cfg_edge.push(( - self.location_table.mid_index(location), - self.location_table.start_index(location.successor_within_block()), - )); - - // If there are borrows on this now dead local, we need to record them as `killed`. - if let StatementKind::StorageDead(local) = statement.kind { - record_killed_borrows_for_local( - all_facts, - self.borrow_set, - self.location_table, - local, - location, - ); - } - } - - self.super_statement(statement, location); - } - - fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { - // When we see `X = ...`, then kill borrows of - // `(*X).foo` and so forth. - self.record_killed_borrows_for_place(*place, location); - - self.super_assign(place, rvalue, location); - } - - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { - if let Some(all_facts) = self.all_facts { - let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - all_facts.cfg_edge.push(( - self.location_table.start_index(location), - self.location_table.mid_index(location), - )); - - let successor_blocks = terminator.successors(); - all_facts.cfg_edge.reserve(successor_blocks.size_hint().0); - for successor_block in successor_blocks { - all_facts.cfg_edge.push(( - self.location_table.mid_index(location), - self.location_table.start_index(successor_block.start_location()), - )); - } - } - - // A `Call` terminator's return value can be a local which has borrows, - // so we need to record those as `killed` as well. - if let TerminatorKind::Call { destination, .. } = terminator.kind { - self.record_killed_borrows_for_place(destination, location); - } - - self.super_terminator(terminator, location); - } - - fn visit_ascribe_user_ty( - &mut self, - _place: &Place<'tcx>, - _variance: ty::Variance, - _user_ty: &UserTypeProjection, - _location: Location, - ) { - } -} - -impl<'cx, 'tcx> ConstraintGeneration<'cx, 'tcx> { - /// Some variable with type `live_ty` is "regular live" at - /// `location` -- i.e., it may be used later. This means that all - /// regions appearing in the type `live_ty` must be live at - /// `location`. - fn add_regular_live_constraint(&mut self, live_ty: T, location: Location) - where - T: TypeVisitable>, - { - debug!("add_regular_live_constraint(live_ty={:?}, location={:?})", live_ty, location); - - self.infcx.tcx.for_each_free_region(&live_ty, |live_region| { - let vid = live_region.as_var(); - self.liveness_constraints.add_element(vid, location); - }); - } - - /// When recording facts for Polonius, records the borrows on the specified place - /// as `killed`. For example, when assigning to a local, or on a call's return destination. - fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) { - if let Some(all_facts) = self.all_facts { - let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); - - // Depending on the `Place` we're killing: - // - if it's a local, or a single deref of a local, - // we kill all the borrows on the local. - // - if it's a deeper projection, we have to filter which - // of the borrows are killed: the ones whose `borrowed_place` - // conflicts with the `place`. - match place.as_ref() { - PlaceRef { local, projection: &[] } - | PlaceRef { local, projection: &[ProjectionElem::Deref] } => { - debug!( - "Recording `killed` facts for borrows of local={:?} at location={:?}", - local, location - ); - - record_killed_borrows_for_local( - all_facts, - self.borrow_set, - self.location_table, - local, - location, - ); - } - - PlaceRef { local, projection: &[.., _] } => { - // Kill conflicting borrows of the innermost local. - debug!( - "Recording `killed` facts for borrows of \ - innermost projected local={:?} at location={:?}", - local, location - ); - - if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) { - for &borrow_index in borrow_indices { - let places_conflict = places_conflict::places_conflict( - self.infcx.tcx, - self.body, - self.borrow_set[borrow_index].borrowed_place, - place, - places_conflict::PlaceConflictBias::NoOverlap, - ); - - if places_conflict { - let location_index = self.location_table.mid_index(location); - all_facts.loan_killed_at.push((borrow_index, location_index)); - } - } - } - } - } - } - } -} - -/// When recording facts for Polonius, records the borrows on the specified local as `killed`. -fn record_killed_borrows_for_local( - all_facts: &mut AllFacts, - borrow_set: &BorrowSet<'_>, - location_table: &LocationTable, - local: Local, - location: Location, -) { - if let Some(borrow_indices) = borrow_set.local_map.get(&local) { - all_facts.loan_killed_at.reserve(borrow_indices.len()); - for &borrow_index in borrow_indices { - let location_index = location_table.mid_index(location); - all_facts.loan_killed_at.push((borrow_index, location_index)); - } - } -} diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index 315886bbe..041ac75ec 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -122,6 +122,7 @@ rustc_index::newtype_index! { } rustc_index::newtype_index! { + #[orderable] #[debug_format = "ConstraintSccIndex({})"] pub struct ConstraintSccIndex {} } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 8676d2ba7..1bd891bdd 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -10,105 +10,93 @@ use rustc_middle::ty::RegionVid; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces}; use rustc_mir_dataflow::ResultsVisitable; -use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill}; -use rustc_mir_dataflow::{Analysis, Direction, Results}; +use rustc_mir_dataflow::{fmt::DebugWithContext, GenKill}; +use rustc_mir_dataflow::{Analysis, AnalysisDomain, Results}; use std::fmt; use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext}; -/// A tuple with named fields that can hold either the results or the transient state of the -/// dataflow analyses used by the borrow checker. -#[derive(Debug)] -pub struct BorrowckAnalyses { - pub borrows: B, - pub uninits: U, - pub ever_inits: E, -} - /// The results of the dataflow analyses used by the borrow checker. -pub type BorrowckResults<'mir, 'tcx> = BorrowckAnalyses< - Results<'tcx, Borrows<'mir, 'tcx>>, - Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, - Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>, ->; +pub struct BorrowckResults<'mir, 'tcx> { + pub(crate) borrows: Results<'tcx, Borrows<'mir, 'tcx>>, + pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, + pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>, +} /// The transient state of the dataflow analyses used by the borrow checker. -pub type BorrowckFlowState<'mir, 'tcx> = - as ResultsVisitable<'tcx>>::FlowState; - -macro_rules! impl_visitable { - ( $( - $T:ident { $( $field:ident : $A:ident ),* $(,)? } - )* ) => { $( - impl<'tcx, $($A),*, D: Direction> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*> - where - $( $A: Analysis<'tcx, Direction = D>, )* - { - type Direction = D; - type FlowState = $T<$( $A::Domain ),*>; +#[derive(Debug)] +pub struct BorrowckFlowState<'mir, 'tcx> { + pub(crate) borrows: as AnalysisDomain<'tcx>>::Domain, + pub(crate) uninits: as AnalysisDomain<'tcx>>::Domain, + pub(crate) ever_inits: as AnalysisDomain<'tcx>>::Domain, +} - fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { - $T { - $( $field: self.$field.analysis.bottom_value(body) ),* - } - } +impl<'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'mir, 'tcx> { + // All three analyses are forward, but we have to use just one here. + type Direction = as AnalysisDomain<'tcx>>::Direction; + type FlowState = BorrowckFlowState<'mir, 'tcx>; - fn reset_to_block_entry( - &self, - state: &mut Self::FlowState, - block: BasicBlock, - ) { - $( state.$field.clone_from(&self.$field.entry_set_for_block(block)); )* - } + fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { + BorrowckFlowState { + borrows: self.borrows.analysis.bottom_value(body), + uninits: self.uninits.analysis.bottom_value(body), + ever_inits: self.ever_inits.analysis.bottom_value(body), + } + } - fn reconstruct_before_statement_effect( - &mut self, - state: &mut Self::FlowState, - stmt: &mir::Statement<'tcx>, - loc: Location, - ) { - $( self.$field.analysis - .apply_before_statement_effect(&mut state.$field, stmt, loc); )* - } + fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { + state.borrows.clone_from(&self.borrows.entry_set_for_block(block)); + state.uninits.clone_from(&self.uninits.entry_set_for_block(block)); + state.ever_inits.clone_from(&self.ever_inits.entry_set_for_block(block)); + } - fn reconstruct_statement_effect( - &mut self, - state: &mut Self::FlowState, - stmt: &mir::Statement<'tcx>, - loc: Location, - ) { - $( self.$field.analysis - .apply_statement_effect(&mut state.$field, stmt, loc); )* - } + fn reconstruct_before_statement_effect( + &mut self, + state: &mut Self::FlowState, + stmt: &mir::Statement<'tcx>, + loc: Location, + ) { + self.borrows.analysis.apply_before_statement_effect(&mut state.borrows, stmt, loc); + self.uninits.analysis.apply_before_statement_effect(&mut state.uninits, stmt, loc); + self.ever_inits.analysis.apply_before_statement_effect(&mut state.ever_inits, stmt, loc); + } - fn reconstruct_before_terminator_effect( - &mut self, - state: &mut Self::FlowState, - term: &mir::Terminator<'tcx>, - loc: Location, - ) { - $( self.$field.analysis - .apply_before_terminator_effect(&mut state.$field, term, loc); )* - } + fn reconstruct_statement_effect( + &mut self, + state: &mut Self::FlowState, + stmt: &mir::Statement<'tcx>, + loc: Location, + ) { + self.borrows.analysis.apply_statement_effect(&mut state.borrows, stmt, loc); + self.uninits.analysis.apply_statement_effect(&mut state.uninits, stmt, loc); + self.ever_inits.analysis.apply_statement_effect(&mut state.ever_inits, stmt, loc); + } - fn reconstruct_terminator_effect( - &mut self, - state: &mut Self::FlowState, - term: &mir::Terminator<'tcx>, - loc: Location, - ) { - $( self.$field.analysis - .apply_terminator_effect(&mut state.$field, term, loc); )* - } - } - )* } -} + fn reconstruct_before_terminator_effect( + &mut self, + state: &mut Self::FlowState, + term: &mir::Terminator<'tcx>, + loc: Location, + ) { + self.borrows.analysis.apply_before_terminator_effect(&mut state.borrows, term, loc); + self.uninits.analysis.apply_before_terminator_effect(&mut state.uninits, term, loc); + self.ever_inits.analysis.apply_before_terminator_effect(&mut state.ever_inits, term, loc); + } -impl_visitable! { - BorrowckAnalyses { borrows: B, uninits: U, ever_inits: E } + fn reconstruct_terminator_effect( + &mut self, + state: &mut Self::FlowState, + term: &mir::Terminator<'tcx>, + loc: Location, + ) { + self.borrows.analysis.apply_terminator_effect(&mut state.borrows, term, loc); + self.uninits.analysis.apply_terminator_effect(&mut state.uninits, term, loc); + self.ever_inits.analysis.apply_terminator_effect(&mut state.ever_inits, term, loc); + } } rustc_index::newtype_index! { + #[orderable] #[debug_format = "bw{}"] pub struct BorrowIndex {} } @@ -120,24 +108,24 @@ rustc_index::newtype_index! { /// `BorrowIndex`, and maps each such index to a `BorrowData` /// describing the borrow. These indexes are used for representing the /// borrows in compact bitvectors. -pub struct Borrows<'a, 'tcx> { +pub struct Borrows<'mir, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, + body: &'mir Body<'tcx>, - borrow_set: &'a BorrowSet<'tcx>, + borrow_set: &'mir BorrowSet<'tcx>, borrows_out_of_scope_at_location: FxIndexMap>, } -struct OutOfScopePrecomputer<'a, 'tcx> { +struct OutOfScopePrecomputer<'mir, 'tcx> { visited: BitSet, visit_stack: Vec, - body: &'a Body<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, + body: &'mir Body<'tcx>, + regioncx: &'mir RegionInferenceContext<'tcx>, borrows_out_of_scope_at_location: FxIndexMap>, } -impl<'a, 'tcx> OutOfScopePrecomputer<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self { +impl<'mir, 'tcx> OutOfScopePrecomputer<'mir, 'tcx> { + fn new(body: &'mir Body<'tcx>, regioncx: &'mir RegionInferenceContext<'tcx>) -> Self { OutOfScopePrecomputer { visited: BitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], @@ -240,17 +228,17 @@ pub fn calculate_borrows_out_of_scope_at_location<'tcx>( prec.borrows_out_of_scope_at_location } -struct PoloniusOutOfScopePrecomputer<'a, 'tcx> { +struct PoloniusOutOfScopePrecomputer<'mir, 'tcx> { visited: BitSet, visit_stack: Vec, - body: &'a Body<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, + body: &'mir Body<'tcx>, + regioncx: &'mir RegionInferenceContext<'tcx>, loans_out_of_scope_at_location: FxIndexMap>, } -impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self { +impl<'mir, 'tcx> PoloniusOutOfScopePrecomputer<'mir, 'tcx> { + fn new(body: &'mir Body<'tcx>, regioncx: &'mir RegionInferenceContext<'tcx>) -> Self { Self { visited: BitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], @@ -403,12 +391,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { } } -impl<'a, 'tcx> Borrows<'a, 'tcx> { +impl<'mir, 'tcx> Borrows<'mir, 'tcx> { pub fn new( tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - regioncx: &'a RegionInferenceContext<'tcx>, - borrow_set: &'a BorrowSet<'tcx>, + body: &'mir Body<'tcx>, + regioncx: &'mir RegionInferenceContext<'tcx>, + borrow_set: &'mir BorrowSet<'tcx>, ) -> Self { let mut borrows_out_of_scope_at_location = calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set); @@ -431,7 +419,8 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { assert_eq!( borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location, - "the loans out of scope must be the same as the borrows out of scope" + "polonius loan scopes differ from NLL borrow scopes, for body {:?}", + body.span, ); borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location; @@ -596,7 +585,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { fn before_terminator_effect( &mut self, - trans: &mut impl GenKill, + trans: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -623,7 +612,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { fn call_return_effect( &mut self, - _trans: &mut impl GenKill, + _trans: &mut Self::Domain, _block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, ) { diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index cfcf31fce..924e68fa9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -10,6 +10,8 @@ use rustc_infer::infer::RegionVariableOrigin; use rustc_infer::infer::{InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::RePlaceholder; +use rustc_middle::ty::Region; use rustc_middle::ty::RegionVid; use rustc_middle::ty::UniverseIndex; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; @@ -205,6 +207,8 @@ trait TypeOpInfo<'tcx> { let span = cause.span; let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region); + debug!(?nice_error); + if let Some(nice_error) = nice_error { mbcx.buffer_error(nice_error); } else { @@ -381,7 +385,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>( error_region: Option>, ) -> Option> { // We generally shouldn't have errors here because the query was - // already run, but there's no point using `delay_span_bug` + // already run, but there's no point using `span_delayed_bug` // when we're going to emit an error here anyway. let _errors = ocx.select_all_or_error(); let region_constraints = ocx.infcx.with_region_constraints(|r| r.clone()); @@ -404,19 +408,41 @@ fn try_extract_error_from_region_constraints<'tcx>( mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin, mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex, ) -> Option> { - let (sub_region, cause) = - region_constraints.constraints.iter().find_map(|(constraint, cause)| { - match *constraint { - Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => { - Some((sub, cause.clone())) - } - // FIXME: Should this check the universe of the var? - Constraint::VarSubReg(vid, sup) if sup == placeholder_region => { - Some((ty::Region::new_var(infcx.tcx, vid), cause.clone())) - } - _ => None, + let matches = + |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) { + (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound, + _ => a_region == b_region, + }; + let check = |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| { + match *constraint { + Constraint::RegSubReg(sub, sup) + if ((exact && sup == placeholder_region) + || (!exact && matches(sup, placeholder_region))) + && sup != sub => + { + Some((sub, cause.clone())) + } + // FIXME: Should this check the universe of the var? + Constraint::VarSubReg(vid, sup) + if ((exact && sup == placeholder_region) + || (!exact && matches(sup, placeholder_region))) => + { + Some((ty::Region::new_var(infcx.tcx, vid), cause.clone())) } - })?; + _ => None, + } + }; + let mut info = region_constraints + .constraints + .iter() + .find_map(|(constraint, cause)| check(constraint, cause, true)); + if info.is_none() { + info = region_constraints + .constraints + .iter() + .find_map(|(constraint, cause)| check(constraint, cause, false)); + } + let (sub_region, cause) = info?; debug!(?sub_region, "cause = {:#?}", cause); let error = match (error_region, *sub_region) { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9a8f1c97e..db0f4559a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -18,7 +18,7 @@ use rustc_middle::mir::{ PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty}; +use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty, TyCtxt}; use rustc_middle::util::CallKind; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::def_id::LocalDefId; @@ -398,7 +398,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } let typeck = self.infcx.tcx.typeck(self.mir_def_id()); let hir_id = hir.parent_id(expr.hir_id); - if let Some(parent) = hir.find(hir_id) { + if let Some(parent) = self.infcx.tcx.opt_hir_node(hir_id) { let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id) @@ -413,7 +413,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { (None, &[][..], 0) }; if let Some(def_id) = def_id - && let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id)) + && let Some(node) = self + .infcx + .tcx + .opt_hir_node(self.infcx.tcx.local_def_id_to_hir_id(def_id)) && let Some(fn_sig) = node.fn_sig() && let Some(ident) = node.ident() && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) @@ -445,7 +448,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { && let hir::ExprKind::Path(hir::QPath::LangItem( LangItem::IntoIterIntoIter, _, - _, )) = call_expr.kind { // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing. @@ -490,7 +492,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut spans = vec![]; for init_idx in inits { let init = &self.move_data.inits[*init_idx]; - let span = init.span(&self.body); + let span = init.span(self.body); if !span.is_dummy() { spans.push(span); } @@ -518,7 +520,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let body = map.body(body_id); let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; - visitor.visit_body(&body); + visitor.visit_body(body); let mut show_assign_sugg = false; let isnt_initialized = if let InitializationRequiringAction::PartialAssignment @@ -614,7 +616,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } let mut visitor = LetVisitor { decl_span, sugg_span: None }; - visitor.visit_body(&body); + visitor.visit_body(body); if let Some(span) = visitor.sugg_span { self.suggest_assign_value(&mut err, moved_place, span); } @@ -779,7 +781,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return; }; // Try to find predicates on *generic params* that would allow copying `ty` - let ocx = ObligationCtxt::new(&self.infcx); + let ocx = ObligationCtxt::new(self.infcx); let copy_did = tcx.require_lang_item(LangItem::Copy, Some(span)); let cause = ObligationCause::misc(span, self.mir_def_id()); @@ -856,7 +858,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.explain_why_borrow_contains_point(location, borrow, None) .add_explanation_to_diagnostic( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &mut err, "", @@ -903,7 +905,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.explain_why_borrow_contains_point(location, borrow, None) .add_explanation_to_diagnostic( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &mut err, "", @@ -1136,7 +1138,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }); } else { issued_spans.var_subdiag( - Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic), + Some(self.infcx.tcx.sess.dcx()), &mut err, Some(issued_borrow.kind), |kind, var_span| { @@ -1153,7 +1155,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); borrow_spans.var_subdiag( - Some(&self.infcx.tcx.sess.parse_sess.span_diagnostic), + Some(self.infcx.tcx.sess.dcx()), &mut err, Some(gen_borrow_kind), |kind, var_span| { @@ -1174,7 +1176,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { explanation.add_explanation_to_diagnostic( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &mut err, first_borrow_desc, @@ -1318,7 +1320,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let tcx = self.infcx.tcx; let hir = tcx.hir(); - let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return }; + let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; let typeck_results = tcx.typeck(self.mir_def_id()); struct ExprFinder<'hir> { @@ -1346,11 +1348,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // }; // corresponding to the desugaring of a for loop `for in { }`. if let hir::ExprKind::Call(path, [arg]) = ex.kind - && let hir::ExprKind::Path(hir::QPath::LangItem( - LangItem::IntoIterIntoIter, - _, - _, - )) = path.kind + && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) = + path.kind && arg.span.contains(self.issue_span) { // Find `IntoIterator::into_iter()` @@ -1368,10 +1367,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .. }) = stmt.kind && let hir::ExprKind::Call(path, _args) = call.kind - && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _)) = + && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _)) = path.kind && let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind - && let hir::QPath::LangItem(LangItem::OptionSome, pat_span, _) = path + && let hir::QPath::LangItem(LangItem::OptionSome, pat_span) = path && call.span.contains(self.issue_span) { // Find `` and the span for the whole `for` loop. @@ -1513,7 +1512,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let local_ty = self.body.local_decls[local].ty; // Get the body the error happens in - let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return }; + let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; let body_expr = hir.body(body_id).value; @@ -1562,7 +1561,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Check that the parent of the closure is a method call, // with receiver matching with local's type (modulo refs) let parent = hir.parent_id(closure_expr.hir_id); - if let hir::Node::Expr(parent) = hir.get(parent) { + if let hir::Node::Expr(parent) = tcx.hir_node(parent) { if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind { let recv_ty = typeck_results.expr_ty(recv); @@ -1578,8 +1577,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return; }; let sig = args.as_closure().sig(); - let tupled_params = - tcx.erase_late_bound_regions(sig.inputs().iter().next().unwrap().map_bound(|&b| b)); + let tupled_params = tcx.instantiate_bound_regions_with_erased( + sig.inputs().iter().next().unwrap().map_bound(|&b| b), + ); let ty::Tuple(params) = tupled_params.kind() else { return }; // Find the first argument with a matching type, get its name @@ -1638,15 +1638,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { issued_spans: &UseSpans<'tcx>, ) { let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return }; - let hir = self.infcx.tcx.hir(); - struct ExpressionFinder<'hir> { + struct ExpressionFinder<'tcx> { capture_span: Span, closure_change_spans: Vec, closure_arg_span: Option, in_closure: bool, suggest_arg: String, - hir: rustc_middle::hir::map::Map<'hir>, + tcx: TyCtxt<'tcx>, closure_local_id: Option, closure_call_changes: Vec<(Span, String)>, } @@ -1660,7 +1659,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn_decl: hir::FnDecl { inputs, .. }, .. }) = e.kind - && let Some(hir::Node::Expr(body)) = self.hir.find(body.hir_id) + && let Some(hir::Node::Expr(body)) = self.tcx.opt_hir_node(body.hir_id) { self.suggest_arg = "this: &Self".to_string(); if inputs.len() > 0 { @@ -1725,8 +1724,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. - })) = hir.find(self.mir_hir_id()) - && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) + })) = self.infcx.tcx.opt_hir_node(self.mir_hir_id()) + && let Some(hir::Node::Expr(expr)) = self.infcx.tcx.opt_hir_node(body_id.hir_id) { let mut finder = ExpressionFinder { capture_span: *capture_kind_span, @@ -1736,7 +1735,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { suggest_arg: String::new(), closure_local_id: None, closure_call_changes: vec![], - hir, + tcx: self.infcx.tcx, }; finder.visit_expr(expr); @@ -1931,7 +1930,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let place_desc = self.describe_place(borrow.borrowed_place.as_ref()); let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0)); - let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place); + let explanation = self.explain_why_borrow_contains_point(location, borrow, kind_place); debug!(?place_desc, ?explanation); @@ -2000,14 +1999,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( location, &name, - &borrow, + borrow, drop_span, borrow_spans, explanation, ), (None, explanation) => self.report_temporary_value_does_not_live_long_enough( location, - &borrow, + borrow, drop_span, borrow_spans, proper_span, @@ -2075,8 +2074,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .map(|name| format!("function `{name}`")) .unwrap_or_else(|| { match &self.infcx.tcx.def_kind(self.mir_def_id()) { + DefKind::Closure + if self + .infcx + .tcx + .is_coroutine(self.mir_def_id().to_def_id()) => + { + "enclosing coroutine" + } DefKind::Closure => "enclosing closure", - DefKind::Coroutine => "enclosing coroutine", kind => bug!("expected closure or coroutine, found {:?}", kind), } .to_string() @@ -2097,7 +2103,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { explanation.add_explanation_to_diagnostic( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &mut err, "", @@ -2118,7 +2124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { explanation.add_explanation_to_diagnostic( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &mut err, "", @@ -2179,7 +2185,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { explanation.add_explanation_to_diagnostic( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &mut err, "", @@ -2290,7 +2296,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let proper_span = proper_span.source_callsite(); if let Some(scope) = self.body.source_scopes.get(source_info.scope) && let ClearCrossCrate::Set(scope_data) = &scope.local_data - && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root) + && let Some(node) = self.infcx.tcx.opt_hir_node(scope_data.lint_root) && let Some(id) = node.body_id() && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind { @@ -2348,7 +2354,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Applicability::MaybeIncorrect, ); } else { - err.note("the result of `format_args!` can only be assigned directly if no placeholders in it's arguments are used"); + err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used"); err.note("to learn more, visit "); } suggested = true; @@ -2364,7 +2370,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } explanation.add_explanation_to_diagnostic( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &mut err, "", @@ -2513,12 +2519,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { CoroutineKind::Gen(kind) => match kind { CoroutineSource::Block => "gen block", CoroutineSource::Closure => "gen closure", - _ => bug!("gen block/closure expected, but gen function found."), + CoroutineSource::Fn => { + bug!("gen block/closure expected, but gen function found.") + } + }, + CoroutineKind::AsyncGen(kind) => match kind { + CoroutineSource::Block => "async gen block", + CoroutineSource::Closure => "async gen closure", + CoroutineSource::Fn => { + bug!("gen block/closure expected, but gen function found.") + } }, CoroutineKind::Async(async_kind) => match async_kind { CoroutineSource::Block => "async block", CoroutineSource::Closure => "async closure", - _ => bug!("async block/closure expected, but async function found."), + CoroutineSource::Fn => { + bug!("async block/closure expected, but async function found.") + } }, CoroutineKind::Coroutine => "coroutine", }, @@ -2841,7 +2858,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &mut err, "", @@ -3019,7 +3036,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } let mut visitor = FakeReadCauseFinder { place, cause: None }; - visitor.visit_body(&self.body); + visitor.visit_body(self.body); match visitor.cause { Some(FakeReadCause::ForMatchGuard) => Some("match guard"), Some(FakeReadCause::ForIndex) => Some("indexing expression"), @@ -3246,7 +3263,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> Option> { debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig); let is_closure = self.infcx.tcx.is_closure(did.to_def_id()); - let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); + let fn_hir_id = self.infcx.tcx.local_def_id_to_hir_id(did); let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?; // We need to work out which arguments to highlight. We do this by looking diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 8a930ca59..f1e712d81 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -5,12 +5,13 @@ use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_index::IndexSlice; use rustc_infer::infer::NllRegionVariableOrigin; +use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir::{ Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::{self, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, DesugaringKind, Span}; use rustc_trait_selection::traits::error_reporting::FindExprBySpan; @@ -86,7 +87,7 @@ impl<'tcx> BorrowExplanation<'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind && let [hir::PathSegment { ident, args: None, .. }] = p.segments && let hir::def::Res::Local(hir_id) = p.res - && let Some(hir::Node::Pat(pat)) = tcx.hir().find(hir_id) + && let Some(hir::Node::Pat(pat)) = tcx.opt_hir_node(hir_id) { err.span_label(pat.span, format!("binding `{ident}` declared here")); } @@ -290,12 +291,69 @@ impl<'tcx> BorrowExplanation<'tcx> { } } + if let ConstraintCategory::Cast { unsize_to: Some(unsize_ty) } = category { + self.add_object_lifetime_default_note(tcx, err, unsize_ty); + } self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); } _ => {} } } + fn add_object_lifetime_default_note( + &self, + tcx: TyCtxt<'tcx>, + err: &mut Diagnostic, + unsize_ty: Ty<'tcx>, + ) { + if let ty::Adt(def, args) = unsize_ty.kind() { + // We try to elaborate the object lifetime defaults and present those to the user. This should + // make it clear where the region constraint is coming from. + let generics = tcx.generics_of(def.did()); + + let mut has_dyn = false; + let mut failed = false; + + let elaborated_args = std::iter::zip(*args, &generics.params).map(|(arg, param)| { + if let Some(ty::Dynamic(obj, _, ty::DynKind::Dyn)) = arg.as_type().map(Ty::kind) { + let default = tcx.object_lifetime_default(param.def_id); + + let re_static = tcx.lifetimes.re_static; + + let implied_region = match default { + // This is not entirely precise. + ObjectLifetimeDefault::Empty => re_static, + ObjectLifetimeDefault::Ambiguous => { + failed = true; + re_static + } + ObjectLifetimeDefault::Param(param_def_id) => { + let index = generics.param_def_id_to_index[¶m_def_id] as usize; + args.get(index).and_then(|arg| arg.as_region()).unwrap_or_else(|| { + failed = true; + re_static + }) + } + ObjectLifetimeDefault::Static => re_static, + }; + + has_dyn = true; + + Ty::new_dynamic(tcx, obj, implied_region, ty::DynKind::Dyn).into() + } else { + arg + } + }); + let elaborated_ty = Ty::new_adt(tcx, *def, tcx.mk_args_from_iter(elaborated_args)); + + if has_dyn && !failed { + err.note(format!( + "due to object lifetime defaults, `{unsize_ty}` actually means `{elaborated_ty}`" + )); + } + } + } + fn add_lifetime_bound_suggestion_to_diagnostic( &self, err: &mut Diagnostic, @@ -364,7 +422,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { kind_place: Option<(WriteKind, Place<'tcx>)>, ) -> BorrowExplanation<'tcx> { let regioncx = &self.regioncx; - let body: &Body<'_> = &self.body; + let body: &Body<'_> = self.body; let tcx = self.infcx.tcx; let borrow_region_vid = borrow.region; diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index c4323fef9..ee3213654 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -10,7 +10,8 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::CoroutineKind; use rustc_index::IndexSlice; -use rustc_infer::infer::LateBoundRegionConversionTime; +use rustc_infer::infer::BoundRegionConversionTime; +use rustc_infer::traits::{FulfillmentErrorCode, SelectionError}; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location, @@ -24,10 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; use rustc_span::def_id::LocalDefId; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx}; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{ - type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause, -}; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -124,7 +124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let did = did.expect_local(); if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { diag.eager_subdiagnostic( - &self.infcx.tcx.sess.parse_sess.span_diagnostic, + self.infcx.tcx.sess.dcx(), OnClosureNote::InvokedTwice { place_name: &ty::place_to_string_for_capture( self.infcx.tcx, @@ -146,7 +146,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let did = did.expect_local(); if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { diag.eager_subdiagnostic( - &self.infcx.tcx.sess.parse_sess.span_diagnostic, + self.infcx.tcx.sess.dcx(), OnClosureNote::MovedTwice { place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), span: *span, @@ -217,9 +217,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { projection: place.projection.split_at(index + 1).0, }) { let var_index = field.index(); - buf = self.upvars[var_index].place.to_string(self.infcx.tcx); + buf = self.upvars[var_index].to_string(self.infcx.tcx); ok = Ok(()); - if !self.upvars[var_index].by_ref { + if !self.upvars[var_index].is_by_ref() { buf.insert(0, '*'); } } else { @@ -250,7 +250,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { local, projection: place.projection.split_at(index + 1).0, }) { - buf = self.upvars[field.index()].place.to_string(self.infcx.tcx); + buf = self.upvars[field.index()].to_string(self.infcx.tcx); ok = Ok(()); } else { let field_name = self.describe_field( @@ -354,7 +354,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ty::Adt(def, _) => { let variant = if let Some(idx) = variant_index { assert!(def.is_enum()); - &def.variant(idx) + def.variant(idx) } else { def.non_enum_variant() }; @@ -462,7 +462,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // lifetimes without names with the value `'0`. if let ty::Ref(region, ..) = ty.kind() { match **region { - ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) + ty::ReBound(_, ty::BoundRegion { kind: br, .. }) | ty::RePlaceholder(ty::PlaceholderRegion { bound: ty::BoundRegion { kind: br, .. }, .. @@ -482,7 +482,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let region = if let ty::Ref(region, ..) = ty.kind() { match **region { - ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) + ty::ReBound(_, ty::BoundRegion { kind: br, .. }) | ty::RePlaceholder(ty::PlaceholderRegion { bound: ty::BoundRegion { kind: br, .. }, .. @@ -624,7 +624,7 @@ impl UseSpans<'_> { /// Add a subdiagnostic to the use of the captured variable, if it exists. pub(super) fn var_subdiag( self, - handler: Option<&rustc_errors::Handler>, + dcx: Option<&rustc_errors::DiagCtxt>, err: &mut Diagnostic, kind: Option, f: impl FnOnce(Option, Span) -> CaptureVarCause, @@ -646,7 +646,7 @@ impl UseSpans<'_> { }); }; let diag = f(coroutine_kind, path_span); - match handler { + match dcx { Some(hd) => err.eager_subdiagnostic(hd, diag), None => err.subdiagnostic(diag), }; @@ -851,7 +851,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { { let Some((method_did, method_args)) = rustc_middle::util::find_self_call( self.infcx.tcx, - &self.body, + self.body, target_temp, location.block, ) else { @@ -958,7 +958,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "closure_span: def_id={:?} target_place={:?} places={:?}", def_id, target_place, places ); - let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(def_id); + let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id); let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr { @@ -1043,12 +1043,43 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } CallKind::Normal { self_arg, desugaring, method_did, method_args } => { let self_arg = self_arg.unwrap(); + let mut has_sugg = false; let tcx = self.infcx.tcx; + // Avoid pointing to the same function in multiple different + // error messages. + if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { + self.explain_iterator_advancement_in_for_loop_if_applicable( + err, + span, + &move_spans, + ); + + let func = tcx.def_path_str(method_did); + err.subdiagnostic(CaptureReasonNote::FuncTakeSelf { + func, + place_name: place_name.clone(), + span: self_arg.span, + }); + } + let parent_did = tcx.parent(method_did); + let parent_self_ty = + matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. }) + .then_some(parent_did) + .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() { + ty::Adt(def, ..) => Some(def.did()), + _ => None, + }); + let is_option_or_result = parent_self_ty.is_some_and(|def_id| { + matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) + }); + if is_option_or_result && maybe_reinitialized_locations_is_empty { + err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span }); + } if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { let ty = moved_place.ty(self.body, tcx).ty; let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) { Some(def_id) => type_known_to_meet_bound_modulo_regions( - &self.infcx, + self.infcx, self.param_env, Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty), def_id, @@ -1108,72 +1139,92 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Erase and shadow everything that could be passed to the new infcx. let ty = moved_place.ty(self.body, tcx).ty; - if let ty::Adt(def, args) = ty.kind() + if let ty::Adt(def, args) = ty.peel_refs().kind() && Some(def.did()) == tcx.lang_items().pin_type() && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind() && let self_ty = self.infcx.instantiate_binder_with_fresh_vars( fn_call_span, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, tcx.fn_sig(method_did).instantiate(tcx, method_args).input(0), ) && self.infcx.can_eq(self.param_env, ty, self_ty) { err.eager_subdiagnostic( - &self.infcx.tcx.sess.parse_sess.span_diagnostic, + self.infcx.tcx.sess.dcx(), CaptureReasonSuggest::FreshReborrow { span: move_span.shrink_to_hi(), }, ); + has_sugg = true; } - if let Some(clone_trait) = tcx.lang_items().clone_trait() - && let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty]) - && let o = Obligation::new( - tcx, - ObligationCause::dummy(), - self.param_env, - ty::Binder::dummy(trait_ref), - ) - && self.infcx.predicate_must_hold_modulo_regions(&o) - { - err.span_suggestion_verbose( - move_span.shrink_to_hi(), - "you can `clone` the value and consume it, but this might not be \ - your desired behavior", - ".clone()".to_string(), - Applicability::MaybeIncorrect, - ); + if let Some(clone_trait) = tcx.lang_items().clone_trait() { + let sugg = if moved_place + .iter_projections() + .any(|(_, elem)| matches!(elem, ProjectionElem::Deref)) + { + vec![ + // We use the fully-qualified path because `.clone()` can + // sometimes choose `<&T as Clone>` instead of `` + // when going through auto-deref, so this ensures that doesn't + // happen, causing suggestions for `.clone().clone()`. + (move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")), + (move_span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![(move_span.shrink_to_hi(), ".clone()".to_string())] + }; + if let Some(errors) = + self.infcx.could_impl_trait(clone_trait, ty, self.param_env) + && !has_sugg + { + let msg = match &errors[..] { + [] => "you can `clone` the value and consume it, but this \ + might not be your desired behavior" + .to_string(), + [error] => { + format!( + "you could `clone` the value and consume it, if \ + the `{}` trait bound could be satisfied", + error.obligation.predicate, + ) + } + [errors @ .., last] => { + format!( + "you could `clone` the value and consume it, if \ + the following trait bounds could be satisfied: {} \ + and `{}`", + errors + .iter() + .map(|e| format!("`{}`", e.obligation.predicate)) + .collect::>() + .join(", "), + last.obligation.predicate, + ) + } + }; + err.multipart_suggestion_verbose( + msg, + sugg, + Applicability::MaybeIncorrect, + ); + for error in errors { + if let FulfillmentErrorCode::CodeSelectionError( + SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( + pred, + )) = error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + err, + error.obligation.predicate.kind().rebind(pred), + ); + } + } + } } } - // Avoid pointing to the same function in multiple different - // error messages. - if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { - self.explain_iterator_advancement_in_for_loop_if_applicable( - err, - span, - &move_spans, - ); - - let func = tcx.def_path_str(method_did); - err.subdiagnostic(CaptureReasonNote::FuncTakeSelf { - func, - place_name, - span: self_arg.span, - }); - } - let parent_did = tcx.parent(method_did); - let parent_self_ty = - matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. }) - .then_some(parent_did) - .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() { - ty::Adt(def, ..) => Some(def.did()), - _ => None, - }); - let is_option_or_result = parent_self_ty.is_some_and(|def_id| { - matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) - }); - if is_option_or_result && maybe_reinitialized_locations_is_empty { - err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span }); - } } // Other desugarings takes &self, which cannot cause a move _ => {} diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 41d6b98d7..43487b85a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -321,7 +321,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let deref_base = match deref_target_place.projection.as_ref() { [proj_base @ .., ProjectionElem::Deref] => { - PlaceRef { local: deref_target_place.local, projection: &proj_base } + PlaceRef { local: deref_target_place.local, projection: proj_base } } _ => bug!("deref_target_place is not a deref projection"), }; @@ -363,8 +363,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { format!("captured variable in an `{closure_kind}` closure"); let upvar = &self.upvars[upvar_field.unwrap().index()]; - let upvar_hir_id = upvar.place.get_root_variable(); - let upvar_name = upvar.place.to_string(self.infcx.tcx); + let upvar_hir_id = upvar.get_root_variable(); + let upvar_name = upvar.to_string(self.infcx.tcx); let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id); let place_name = self.describe_any_place(move_place.as_ref()); @@ -583,7 +583,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, ty: bind_to.ty, - place: &place_desc, + place: place_desc, span: binding_span, }); } @@ -607,7 +607,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(adt) = local_ty.ty_adt_def() && adt.repr().packed() - && let ExpnKind::Macro(MacroKind::Derive, name) = self.body.span.ctxt().outer_expn_data().kind + && let ExpnKind::Macro(MacroKind::Derive, name) = + self.body.span.ctxt().outer_expn_data().kind { err.note(format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour")); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index dde46eef6..c3c1f1293 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -3,8 +3,9 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; +use rustc_infer::traits; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; -use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt}; +use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt}; use rustc_middle::{ hir::place::PlaceBase, mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location}, @@ -12,6 +13,8 @@ use rustc_middle::{ use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, BytePos, DesugaringKind, Span}; use rustc_target::abi::FieldIdx; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use crate::diagnostics::BorrowedContentSource; use crate::util::FindAssignments; @@ -66,7 +69,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { )); let imm_borrow_derefed = self.upvars[upvar_index.index()] - .place .place .deref_tys() .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not))); @@ -85,7 +87,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if self.is_upvar_field_projection(access_place.as_ref()).is_some() { reason = ", as it is not declared as mutable".to_string(); } else { - let name = self.upvars[upvar_index.index()].place.to_string(self.infcx.tcx); + let name = self.upvars[upvar_index.index()].to_string(self.infcx.tcx); reason = format!(", as `{name}` is not declared as mutable"); } } @@ -388,13 +390,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); - let captured_place = &self.upvars[upvar_index.index()].place; + let captured_place = self.upvars[upvar_index.index()]; err.span_label(span, format!("cannot {act}")); let upvar_hir_id = captured_place.get_root_variable(); - if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) + if let Some(Node::Pat(pat)) = self.infcx.tcx.opt_hir_node(upvar_hir_id) && let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, upvar_ident, _) = pat.kind { @@ -659,9 +661,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if self.body.local_kind(local) != LocalKind::Arg { return (false, None); } - let hir_map = self.infcx.tcx.hir(); let my_def = self.body.source.def_id(); - let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap()); + let my_hir = self.infcx.tcx.local_def_id_to_hir_id(my_def.as_local().unwrap()); let Some(td) = self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x)) else { @@ -669,7 +670,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; ( true, - td.as_local().and_then(|tld| match hir_map.find_by_def_id(tld) { + td.as_local().and_then(|tld| match self.infcx.tcx.opt_hir_node_by_def_id(tld) { Some(Node::Item(hir::Item { kind: hir::ItemKind::Trait(_, _, _, _, items), .. @@ -680,25 +681,27 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if !matches!(k, hir::AssocItemKind::Fn { .. }) { continue; } - if hir_map.name(hi) != hir_map.name(my_hir) { + if self.infcx.tcx.hir().name(hi) != self.infcx.tcx.hir().name(my_hir) { continue; } f_in_trait_opt = Some(hi); break; } - f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) { - Some(Node::TraitItem(hir::TraitItem { - kind: - hir::TraitItemKind::Fn( - hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. }, - _, - ), - .. - })) => { - let hir::Ty { span, .. } = inputs[local.index() - 1]; - Some(span) + f_in_trait_opt.and_then(|f_in_trait| { + match self.infcx.tcx.opt_hir_node(f_in_trait) { + Some(Node::TraitItem(hir::TraitItem { + kind: + hir::TraitItemKind::Fn( + hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. }, + _, + ), + .. + })) => { + let hir::Ty { span, .. } = inputs[local.index() - 1]; + Some(span) + } + _ => None, } - _ => None, }) } _ => None, @@ -739,12 +742,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - let hir_map = self.infcx.tcx.hir(); let def_id = self.body.source.def_id(); let hir_id = if let Some(local_def_id) = def_id.as_local() - && let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) + && let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) { - let body = hir_map.body(body_id); + let body = self.infcx.tcx.hir().body(body_id); let mut v = BindingFinder { span: pat_span, hir_id: None }; v.visit_body(body); v.hir_id @@ -760,7 +762,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && let Some(hir::Node::Local(hir::Local { pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, .. - })) = hir_map.find(hir_id) + })) = self.infcx.tcx.opt_hir_node(hir_id) && let Ok(name) = self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span) { @@ -940,7 +942,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let closure_id = self.mir_hir_id(); let closure_span = self.infcx.tcx.def_span(self.mir_def_id()); let fn_call_id = hir.parent_id(closure_id); - let node = hir.get(fn_call_id); + let node = self.infcx.tcx.hir_node(fn_call_id); let def_id = hir.enclosing_body_owner(fn_call_id); let mut look_at_return = true; // If we can detect the expression to be an `fn` call where the closure was an argument, @@ -999,7 +1001,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if look_at_return && hir.get_return_block(closure_id).is_some() { // ...otherwise we are probably in the tail expression of the function, point at the // return type. - match hir.get_by_def_id(hir.get_parent_item(fn_call_id).def_id) { + match self.infcx.tcx.hir_node_by_def_id(hir.get_parent_item(fn_call_id).def_id) { hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }) | hir::Node::TraitItem(hir::TraitItem { ident, @@ -1197,12 +1199,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { hir::intravisit::walk_stmt(self, s); } } - let hir_map = self.infcx.tcx.hir(); let def_id = self.body.source.def_id(); let hir_id = if let Some(local_def_id) = def_id.as_local() - && let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) + && let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) { - let body = hir_map.body(body_id); + let body = self.infcx.tcx.hir().body(body_id); let mut v = BindingFinder { span: err_label_span, hir_id: None }; v.visit_body(body); v.hir_id @@ -1211,8 +1212,105 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(hir_id) = hir_id - && let Some(hir::Node::Local(local)) = hir_map.find(hir_id) + && let Some(hir::Node::Local(local)) = self.infcx.tcx.opt_hir_node(hir_id) { + let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); + if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() + && let Some(expr) = local.init + && let ty = tables.node_type_opt(expr.hir_id) + && let Some(ty) = ty + && let ty::Ref(..) = ty.kind() + { + match self + .infcx + .could_impl_trait(clone_trait, ty.peel_refs(), self.param_env) + .as_deref() + { + Some([]) => { + // The type implements Clone. + err.span_help( + expr.span, + format!( + "you can `clone` the `{}` value and consume it, but this \ + might not be your desired behavior", + ty.peel_refs(), + ), + ); + } + None => { + if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = + expr.kind + && segment.ident.name == sym::clone + { + err.span_help( + span, + format!( + "`{}` doesn't implement `Clone`, so this call clones \ + the reference `{ty}`", + ty.peel_refs(), + ), + ); + } + // The type doesn't implement Clone. + let trait_ref = ty::Binder::dummy(ty::TraitRef::new( + self.infcx.tcx, + clone_trait, + [ty.peel_refs()], + )); + let obligation = traits::Obligation::new( + self.infcx.tcx, + traits::ObligationCause::dummy(), + self.param_env, + trait_ref, + ); + self.infcx.err_ctxt().suggest_derive( + &obligation, + err, + trait_ref.to_predicate(self.infcx.tcx), + ); + } + Some(errors) => { + if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = + expr.kind + && segment.ident.name == sym::clone + { + err.span_help( + span, + format!( + "`{}` doesn't implement `Clone` because its \ + implementations trait bounds could not be met, so \ + this call clones the reference `{ty}`", + ty.peel_refs(), + ), + ); + err.note(format!( + "the following trait bounds weren't met: {}", + errors + .iter() + .map(|e| e.obligation.predicate.to_string()) + .collect::>() + .join("\n"), + )); + } + // The type doesn't implement Clone because of unmet obligations. + for error in errors { + if let traits::FulfillmentErrorCode::CodeSelectionError( + traits::SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( + pred, + )) = error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + err, + error.obligation.predicate.kind().rebind(pred), + ); + } + } + } + } + } let (changing, span, sugg) = match local.ty { Some(ty) => ("changing", ty.span, message), None => { @@ -1397,7 +1495,7 @@ fn get_mut_span_in_struct_field<'tcx>( && let ty::Adt(def, _) = ty.kind() && let field = def.all_fields().nth(field.index())? // Use the HIR types to construct the diagnostic message. - && let node = tcx.hir().find_by_def_id(field.did.as_local()?)? + && let node = tcx.opt_hir_node_by_def_id(field.did.as_local()?)? // Now we're dealing with the actual struct that we're going to suggest a change to, // we can expect a field that is an immutable reference to a type. && let hir::Node::Field(field) = node diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index b6eb9ae98..66275888c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -50,8 +50,8 @@ impl OutlivesSuggestionBuilder { // naming the `'self` lifetime in methods, etc. fn region_name_is_suggestable(name: &RegionName) -> bool { match name.source { - RegionNameSource::NamedEarlyBoundRegion(..) - | RegionNameSource::NamedFreeRegion(..) + RegionNameSource::NamedEarlyParamRegion(..) + | RegionNameSource::NamedLateParamRegion(..) | RegionNameSource::Static => true, // Don't give suggestions for upvars, closure return types, or other unnameable @@ -206,7 +206,7 @@ impl OutlivesSuggestionBuilder { // If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a // list of diagnostics. let mut diag = if suggested.len() == 1 { - mbcx.infcx.tcx.sess.diagnostic().struct_help(match suggested.last().unwrap() { + mbcx.infcx.tcx.sess.dcx().struct_help(match suggested.last().unwrap() { SuggestedConstraint::Outlives(a, bs) => { let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect(); format!("add bound `{a}: {}`", bs.join(" + ")) @@ -223,7 +223,7 @@ impl OutlivesSuggestionBuilder { .infcx .tcx .sess - .diagnostic() + .dcx() .struct_help("the following changes may resolve your lifetime errors"); // Add suggestions. diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index a0a809123..759f5e910 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -35,7 +35,7 @@ use crate::session_diagnostics::{ LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, }; -use super::{OutlivesSuggestionBuilder, RegionName}; +use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; use crate::region_infer::{BlameConstraint, ExtraConstraintInfo}; use crate::{ nll::ConstraintDescription, @@ -53,7 +53,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::Yield => "yielding this value ", ConstraintCategory::UseAsConst => "using this value as a constant ", ConstraintCategory::UseAsStatic => "using this value as a static ", - ConstraintCategory::Cast => "cast ", + ConstraintCategory::Cast { .. } => "cast ", ConstraintCategory::CallArgument(_) => "argument ", ConstraintCategory::TypeAnnotation => "type annotation ", ConstraintCategory::ClosureBounds => "closure body ", @@ -84,7 +84,7 @@ impl<'tcx> RegionErrors<'tcx> { #[track_caller] pub fn push(&mut self, val: impl Into>) { let val = val.into(); - self.1.sess.delay_span_bug(DUMMY_SP, format!("{val:?}")); + self.1.sess.span_delayed_bug(DUMMY_SP, format!("{val:?}")); self.0.push(val); } pub fn is_empty(&self) -> bool { @@ -95,6 +95,12 @@ impl<'tcx> RegionErrors<'tcx> { } } +impl std::fmt::Debug for RegionErrors<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("RegionErrors").field(&self.0).finish() + } +} + #[derive(Clone, Debug)] pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). @@ -181,8 +187,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// Returns `true` if a closure is inferred to be an `FnMut` closure. fn is_closure_fn_mut(&self, fr: RegionVid) -> bool { - if let Some(ty::ReFree(free_region)) = self.to_error_region(fr).as_deref() - && let ty::BoundRegionKind::BrEnv = free_region.bound_region + if let Some(ty::ReLateParam(late_param)) = self.to_error_region(fr).as_deref() + && let ty::BoundRegionKind::BrEnv = late_param.bound_region && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty { return args.as_closure().kind() == ty::ClosureKind::FnMut; @@ -209,7 +215,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .map(|placeholder| { if let Some(id) = placeholder.bound.kind.get_id() && let Some(placeholder_id) = id.as_local() - && let gat_hir_id = hir.local_def_id_to_hir_id(placeholder_id) + && let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id) && let Some(generics_impl) = hir.get_parent(gat_hir_id).generics() { Some((gat_hir_id, generics_impl)) @@ -230,7 +236,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if bound_generic_params .iter() - .rfind(|bgp| hir.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id) + .rfind(|bgp| self.infcx.tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id) .is_some() { for bound in *bounds { @@ -599,7 +605,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; let captured_place = &self.upvars[upvar_field.index()].place; - let defined_hir = match captured_place.place.base { + let defined_hir = match captured_place.base { PlaceBase::Local(hirid) => Some(hirid), PlaceBase::Upvar(upvar) => Some(upvar.var_path.hir_id), _ => None, @@ -644,14 +650,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &self.upvars, errci.fr, ); let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region( self.infcx.tcx, - &self.body, + self.body, &self.local_names, &self.upvars, errci.outlived_fr, @@ -757,7 +763,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let err = LifetimeOutliveErr { span: *span }; let mut diag = self.infcx.tcx.sess.create_err(err); - let fr_name = self.give_region_a_name(*fr).unwrap(); + // In certain scenarios, such as the one described in issue #118021, + // we might encounter a lifetime that cannot be named. + // These situations are bound to result in errors. + // To prevent an immediate ICE, we opt to create a dummy name instead. + let fr_name = self.give_region_a_name(*fr).unwrap_or(RegionName { + name: kw::UnderscoreLifetime, + source: RegionNameSource::Static, + }); fr_name.highlight_region_name(&mut diag); let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap(); outlived_fr_name.highlight_region_name(&mut diag); @@ -958,7 +971,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { for found_did in found_dids { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); - hir_v.visit_ty(&self_ty); + hir_v.visit_ty(self_ty); debug!("trait spans found: {:?}", traits); for span in &traits { let mut multi_span: MultiSpan = vec![*span].into(); @@ -995,7 +1008,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .infcx .tcx .is_suitable_region(sub) - .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) + .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.bound_region)) else { return; }; @@ -1004,7 +1017,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .infcx .tcx .is_suitable_region(sup) - .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) + .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.bound_region)) else { return; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index d38cfbc54..8441dfaa7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -23,14 +23,14 @@ pub(crate) struct RegionName { } /// Denotes the source of a region that is named by a `RegionName`. For example, a free region that -/// was named by the user would get `NamedFreeRegion` and `'static` lifetime would get `Static`. +/// was named by the user would get `NamedLateParamRegion` and `'static` lifetime would get `Static`. /// This helps to print the right kinds of diagnostics. #[derive(Debug, Clone)] pub(crate) enum RegionNameSource { /// A bound (not free) region that was instantiated at the def site (not an HRTB). - NamedEarlyBoundRegion(Span), + NamedEarlyParamRegion(Span), /// A free region that the user has a name (`'a`) for. - NamedFreeRegion(Span), + NamedLateParamRegion(Span), /// The `'static` region. Static, /// The free region corresponding to the environment of a closure. @@ -69,8 +69,8 @@ pub(crate) enum RegionNameHighlight { impl RegionName { pub(crate) fn was_named(&self) -> bool { match self.source { - RegionNameSource::NamedEarlyBoundRegion(..) - | RegionNameSource::NamedFreeRegion(..) + RegionNameSource::NamedEarlyParamRegion(..) + | RegionNameSource::NamedLateParamRegion(..) | RegionNameSource::Static => true, RegionNameSource::SynthesizedFreeEnvRegion(..) | RegionNameSource::AnonRegionFromArgument(..) @@ -85,8 +85,8 @@ impl RegionName { pub(crate) fn span(&self) -> Option { match self.source { RegionNameSource::Static => None, - RegionNameSource::NamedEarlyBoundRegion(span) - | RegionNameSource::NamedFreeRegion(span) + RegionNameSource::NamedEarlyParamRegion(span) + | RegionNameSource::NamedLateParamRegion(span) | RegionNameSource::SynthesizedFreeEnvRegion(span, _) | RegionNameSource::AnonRegionFromUpvar(span, _) | RegionNameSource::AnonRegionFromYieldTy(span, _) @@ -104,8 +104,8 @@ impl RegionName { pub(crate) fn highlight_region_name(&self, diag: &mut Diagnostic) { match &self.source { - RegionNameSource::NamedFreeRegion(span) - | RegionNameSource::NamedEarlyBoundRegion(span) => { + RegionNameSource::NamedLateParamRegion(span) + | RegionNameSource::NamedEarlyParamRegion(span) => { diag.span_label(*span, format!("lifetime `{self}` defined here")); } RegionNameSource::SynthesizedFreeEnvRegion(span, note) => { @@ -199,7 +199,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } pub(crate) fn mir_hir_id(&self) -> hir::HirId { - self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id()) + self.infcx.tcx.local_def_id_to_hir_id(self.mir_def_id()) } /// Generate a synthetic region named `'N`, where `N` is the next value of the counter. Then, @@ -280,28 +280,31 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { debug!("give_region_a_name: error_region = {:?}", error_region); match *error_region { - ty::ReEarlyBound(ebr) => ebr.has_name().then(|| { + ty::ReEarlyParam(ebr) => ebr.has_name().then(|| { let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP); - RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyBoundRegion(span) } + RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) } }), ty::ReStatic => { Some(RegionName { name: kw::StaticLifetime, source: RegionNameSource::Static }) } - ty::ReFree(free_region) => match free_region.bound_region { + ty::ReLateParam(late_param) => match late_param.bound_region { ty::BoundRegionKind::BrNamed(region_def_id, name) => { // Get the span to point to, even if we don't use the name. let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP); debug!( "bound region named: {:?}, is_named: {:?}", name, - free_region.bound_region.is_named() + late_param.bound_region.is_named() ); - if free_region.bound_region.is_named() { + if late_param.bound_region.is_named() { // A named region that is actually named. - Some(RegionName { name, source: RegionNameSource::NamedFreeRegion(span) }) + Some(RegionName { + name, + source: RegionNameSource::NamedLateParamRegion(span), + }) } else if tcx.asyncness(self.mir_hir_id().owner).is_async() { // If we spuriously thought that the region is named, we should let the // system generate a true name for error messages. Currently this can @@ -357,7 +360,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::BoundRegionKind::BrAnon => None, }, - ty::ReLateBound(..) + ty::ReBound(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased @@ -384,7 +387,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { 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( - &self.body, + self.body, &self.local_names, argument_index, ); @@ -616,8 +619,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { _, ) => { // HIR lowering sometimes doesn't catch this in erroneous - // programs, so we need to use delay_span_bug here. See #82126. - self.infcx.tcx.sess.delay_span_bug( + // programs, so we need to use span_delayed_bug here. See #82126. + self.infcx.tcx.sess.span_delayed_bug( hir_arg.span(), format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"), ); @@ -669,7 +672,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let mir_hir_id = self.mir_hir_id(); - let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) { + let (return_span, mir_description, hir_ty) = match tcx.hir_node(mir_hir_id) { hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, body, fn_decl_span, .. }), .. @@ -681,12 +684,12 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)), }; let mir_description = match hir.body(body).coroutine_kind { - Some(hir::CoroutineKind::Async(gen)) => match gen { + Some(hir::CoroutineKind::Async(src)) => match src { hir::CoroutineSource::Block => " of async block", hir::CoroutineSource::Closure => " of async closure", hir::CoroutineSource::Fn => { let parent_item = - hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id); + tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id); let output = &parent_item .fn_decl() .expect("coroutine lowered from async fn should be in fn") @@ -698,12 +701,12 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { " of async function" } }, - Some(hir::CoroutineKind::Gen(gen)) => match gen { + Some(hir::CoroutineKind::Gen(src)) => match src { hir::CoroutineSource::Block => " of gen block", hir::CoroutineSource::Closure => " of gen closure", hir::CoroutineSource::Fn => { let parent_item = - hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id); + tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id); let output = &parent_item .fn_decl() .expect("coroutine lowered from gen fn should be in fn") @@ -712,6 +715,21 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { " of gen function" } }, + + Some(hir::CoroutineKind::AsyncGen(src)) => match src { + hir::CoroutineSource::Block => " of async gen block", + hir::CoroutineSource::Closure => " of async gen closure", + hir::CoroutineSource::Fn => { + let parent_item = + tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id); + let output = &parent_item + .fn_decl() + .expect("coroutine lowered from async gen fn should be in fn") + .output; + span = output.span(); + " of async gen function" + } + }, Some(hir::CoroutineKind::Coroutine) => " of coroutine", None => " of closure", }; @@ -770,28 +788,18 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { }; let opaque_ty = hir.item(id); if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { - bounds: - [ - hir::GenericBound::LangItemTrait( - hir::LangItem::Future, - _, - _, - hir::GenericArgs { - bindings: - [ - hir::TypeBinding { - ident: Ident { name: sym::Output, .. }, - kind: - hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) }, - .. - }, - ], - .. - }, - ), - ], + bounds: [hir::GenericBound::Trait(trait_ref, _)], .. }) = opaque_ty.kind + && let Some(segment) = trait_ref.trait_ref.path.segments.last() + && let Some(args) = segment.args + && let [ + hir::TypeBinding { + ident: Ident { name: sym::Output, .. }, + kind: hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) }, + .. + }, + ] = args.bindings { ty } else { @@ -823,7 +831,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let type_name = self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name; - let yield_span = match tcx.hir().get(self.mir_hir_id()) { + let yield_span = match tcx.hir_node(self.mir_hir_id()) { hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. @@ -847,7 +855,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { + let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else { return None; }; if region.has_name() { @@ -862,7 +870,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let found = tcx .any_free_region_meets(&tcx.type_of(region_parent).instantiate_identity(), |r| { - *r == ty::ReEarlyBound(region) + *r == ty::ReEarlyParam(region) }); Some(RegionName { @@ -881,7 +889,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { + let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else { return None; }; if region.has_name() { @@ -943,7 +951,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { &self, clauses: &[ty::Clause<'tcx>], ty: Ty<'tcx>, - region: ty::EarlyBoundRegion, + region: ty::EarlyParamRegion, ) -> bool { let tcx = self.infcx.tcx; ty.walk().any(|arg| { @@ -956,7 +964,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::ClauseKind::Projection(data) if data.projection_ty.self_ty() == ty => {} _ => return false, } - tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyBound(region)) + tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyParam(region)) }) } else { false diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index 3a104c524..28e07f2a8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -2,10 +2,9 @@ #![deny(rustc::diagnostic_outside_of_impl)] use crate::region_infer::RegionInferenceContext; -use crate::Upvar; use rustc_index::IndexSlice; use rustc_middle::mir::{Body, Local}; -use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -15,7 +14,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { tcx: TyCtxt<'tcx>, body: &Body<'tcx>, local_names: &IndexSlice>, - upvars: &[Upvar<'tcx>], + upvars: &[&ty::CapturedPlace<'tcx>], fr: RegionVid, ) -> Option<(Option, Span)> { debug!("get_var_name_and_span_for_region(fr={fr:?})"); @@ -66,10 +65,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { pub(crate) fn get_upvar_name_and_span_for_region( &self, tcx: TyCtxt<'tcx>, - upvars: &[Upvar<'tcx>], + upvars: &[&ty::CapturedPlace<'tcx>], upvar_index: usize, ) -> (Symbol, Span) { - let upvar_hir_id = upvars[upvar_index].place.get_root_variable(); + let upvar_hir_id = upvars[upvar_index].get_root_variable(); debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}"); let upvar_name = tcx.hir().name(upvar_hir_id); diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs deleted file mode 100644 index ec6d7b74e..000000000 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ /dev/null @@ -1,441 +0,0 @@ -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] -use rustc_data_structures::graph::dominators::Dominators; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, BasicBlock, Body, Location, NonDivergingIntrinsic, Place, Rvalue}; -use rustc_middle::mir::{BorrowKind, Mutability, Operand}; -use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; -use rustc_middle::mir::{Statement, StatementKind}; -use rustc_middle::ty::TyCtxt; - -use crate::{ - borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, path_utils::*, AccessDepth, - Activation, ArtificialField, BorrowIndex, Deep, LocalMutationIsAllowed, Read, ReadKind, - ReadOrWrite, Reservation, Shallow, Write, WriteKind, -}; - -pub(super) fn generate_invalidates<'tcx>( - tcx: TyCtxt<'tcx>, - all_facts: &mut Option, - location_table: &LocationTable, - body: &Body<'tcx>, - borrow_set: &BorrowSet<'tcx>, -) { - if all_facts.is_none() { - // Nothing to do if we don't have any facts - return; - } - - if let Some(all_facts) = all_facts { - let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); - let dominators = body.basic_blocks.dominators(); - let mut ig = InvalidationGenerator { - all_facts, - borrow_set, - tcx, - location_table, - body: &body, - dominators, - }; - ig.visit_body(body); - } -} - -struct InvalidationGenerator<'cx, 'tcx> { - tcx: TyCtxt<'tcx>, - all_facts: &'cx mut AllFacts, - location_table: &'cx LocationTable, - body: &'cx Body<'tcx>, - dominators: &'cx Dominators, - borrow_set: &'cx BorrowSet<'tcx>, -} - -/// Visits the whole MIR and generates `invalidates()` facts. -/// Most of the code implementing this was stolen from `borrow_check/mod.rs`. -impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { - self.check_activations(location); - - match &statement.kind { - StatementKind::Assign(box (lhs, rhs)) => { - self.consume_rvalue(location, rhs); - - self.mutate_place(location, *lhs, Shallow(None)); - } - StatementKind::FakeRead(box (_, _)) => { - // Only relevant for initialized/liveness/safety checks. - } - StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => { - self.consume_operand(location, op); - } - StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { - src, - dst, - count, - })) => { - self.consume_operand(location, src); - self.consume_operand(location, dst); - self.consume_operand(location, count); - } - // Only relevant for mir typeck - StatementKind::AscribeUserType(..) - // Only relevant for liveness and unsafeck - | StatementKind::PlaceMention(..) - // Doesn't have any language semantics - | StatementKind::Coverage(..) - // Does not actually affect borrowck - | StatementKind::StorageLive(..) => {} - StatementKind::StorageDead(local) => { - self.access_place( - location, - Place::from(*local), - (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), - LocalMutationIsAllowed::Yes, - ); - } - StatementKind::ConstEvalCounter - | StatementKind::Nop - | StatementKind::Retag { .. } - | StatementKind::Deinit(..) - | StatementKind::SetDiscriminant { .. } => { - bug!("Statement not allowed in this MIR phase") - } - } - - self.super_statement(statement, location); - } - - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { - self.check_activations(location); - - match &terminator.kind { - TerminatorKind::SwitchInt { discr, targets: _ } => { - self.consume_operand(location, discr); - } - TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => { - let write_kind = - if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop }; - self.access_place( - location, - *drop_place, - (AccessDepth::Drop, Write(write_kind)), - LocalMutationIsAllowed::Yes, - ); - } - TerminatorKind::Call { - func, - args, - destination, - target: _, - unwind: _, - call_source: _, - fn_span: _, - } => { - self.consume_operand(location, func); - for arg in args { - self.consume_operand(location, arg); - } - self.mutate_place(location, *destination, Deep); - } - TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { - self.consume_operand(location, cond); - use rustc_middle::mir::AssertKind; - if let AssertKind::BoundsCheck { len, index } = &**msg { - self.consume_operand(location, len); - self.consume_operand(location, index); - } - } - TerminatorKind::Yield { value, resume, resume_arg, drop: _ } => { - self.consume_operand(location, value); - - // Invalidate all borrows of local places - let borrow_set = self.borrow_set; - let resume = self.location_table.start_index(resume.start_location()); - for (i, data) in borrow_set.iter_enumerated() { - if borrow_of_local_data(data.borrowed_place) { - self.all_facts.loan_invalidated_at.push((resume, i)); - } - } - - self.mutate_place(location, *resume_arg, Deep); - } - TerminatorKind::UnwindResume - | TerminatorKind::Return - | TerminatorKind::CoroutineDrop => { - // Invalidate all borrows of local places - let borrow_set = self.borrow_set; - let start = self.location_table.start_index(location); - for (i, data) in borrow_set.iter_enumerated() { - if borrow_of_local_data(data.borrowed_place) { - self.all_facts.loan_invalidated_at.push((start, i)); - } - } - } - TerminatorKind::InlineAsm { - template: _, - operands, - options: _, - line_spans: _, - destination: _, - unwind: _, - } => { - for op in operands { - match op { - InlineAsmOperand::In { reg: _, value } => { - self.consume_operand(location, value); - } - InlineAsmOperand::Out { reg: _, late: _, place, .. } => { - if let &Some(place) = place { - self.mutate_place(location, place, Shallow(None)); - } - } - InlineAsmOperand::InOut { reg: _, late: _, in_value, out_place } => { - self.consume_operand(location, in_value); - if let &Some(out_place) = out_place { - self.mutate_place(location, out_place, Shallow(None)); - } - } - InlineAsmOperand::Const { value: _ } - | InlineAsmOperand::SymFn { value: _ } - | InlineAsmOperand::SymStatic { def_id: _ } => {} - } - } - } - TerminatorKind::Goto { target: _ } - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Unreachable - | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } - | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { - // no data used, thus irrelevant to borrowck - } - } - - self.super_terminator(terminator, location); - } -} - -impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { - /// Simulates mutation of a place. - fn mutate_place(&mut self, location: Location, place: Place<'tcx>, kind: AccessDepth) { - self.access_place( - location, - place, - (kind, Write(WriteKind::Mutate)), - LocalMutationIsAllowed::ExceptUpvars, - ); - } - - /// Simulates consumption of an operand. - fn consume_operand(&mut self, location: Location, operand: &Operand<'tcx>) { - match *operand { - Operand::Copy(place) => { - self.access_place( - location, - place, - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - ); - } - Operand::Move(place) => { - self.access_place( - location, - place, - (Deep, Write(WriteKind::Move)), - LocalMutationIsAllowed::Yes, - ); - } - Operand::Constant(_) => {} - } - } - - // Simulates consumption of an rvalue - fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) { - match rvalue { - &Rvalue::Ref(_ /*rgn*/, bk, place) => { - let access_kind = match bk { - BorrowKind::Fake => { - (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) - } - BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), - BorrowKind::Mut { .. } => { - let wk = WriteKind::MutableBorrow(bk); - if allow_two_phase_borrow(bk) { - (Deep, Reservation(wk)) - } else { - (Deep, Write(wk)) - } - } - }; - - self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); - } - - &Rvalue::AddressOf(mutability, place) => { - let access_kind = match mutability { - Mutability::Mut => ( - Deep, - Write(WriteKind::MutableBorrow(BorrowKind::Mut { - kind: mir::MutBorrowKind::Default, - })), - ), - Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), - }; - - self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); - } - - Rvalue::ThreadLocalRef(_) => {} - - Rvalue::Use(operand) - | Rvalue::Repeat(operand, _) - | Rvalue::UnaryOp(_ /*un_op*/, operand) - | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) - | Rvalue::ShallowInitBox(operand, _ /*ty*/) => self.consume_operand(location, operand), - - &Rvalue::CopyForDeref(place) => { - let op = &Operand::Copy(place); - self.consume_operand(location, op); - } - - &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { - let af = match rvalue { - Rvalue::Len(..) => Some(ArtificialField::ArrayLength), - Rvalue::Discriminant(..) => None, - _ => unreachable!(), - }; - self.access_place( - location, - place, - (Shallow(af), Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - ); - } - - Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) - | Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => { - self.consume_operand(location, operand1); - self.consume_operand(location, operand2); - } - - Rvalue::NullaryOp(_op, _ty) => {} - - Rvalue::Aggregate(_, operands) => { - for operand in operands { - self.consume_operand(location, operand); - } - } - } - } - - /// Simulates an access to a place. - fn access_place( - &mut self, - location: Location, - place: Place<'tcx>, - kind: (AccessDepth, ReadOrWrite), - _is_local_mutation_allowed: LocalMutationIsAllowed, - ) { - let (sd, rw) = kind; - // note: not doing check_access_permissions checks because they don't generate invalidates - self.check_access_for_conflict(location, place, sd, rw); - } - - fn check_access_for_conflict( - &mut self, - location: Location, - place: Place<'tcx>, - sd: AccessDepth, - rw: ReadOrWrite, - ) { - debug!( - "invalidation::check_access_for_conflict(location={:?}, place={:?}, sd={:?}, \ - rw={:?})", - location, place, sd, rw, - ); - let tcx = self.tcx; - let body = self.body; - let borrow_set = self.borrow_set; - each_borrow_involving_path( - self, - tcx, - body, - location, - (sd, place), - borrow_set, - |_| true, - |this, borrow_index, borrow| { - match (rw, borrow.kind) { - // Obviously an activation is compatible with its own - // reservation (or even prior activating uses of same - // borrow); so don't check if they interfere. - // - // NOTE: *reservations* do conflict with themselves; - // thus aren't injecting unsoundness w/ this check.) - (Activation(_, activating), _) if activating == borrow_index => { - // Activating a borrow doesn't generate any invalidations, since we - // have already taken the reservation - } - - (Read(_), BorrowKind::Fake | BorrowKind::Shared) - | (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => { - // Reads don't invalidate shared or shallow borrows - } - - (Read(_), BorrowKind::Mut { .. }) => { - // Reading from mere reservations of mutable-borrows is OK. - if !is_active(&this.dominators, borrow, location) { - // If the borrow isn't active yet, reads don't invalidate it - assert!(allow_two_phase_borrow(borrow.kind)); - return Control::Continue; - } - - // Unique and mutable borrows are invalidated by reads from any - // involved path - this.emit_loan_invalidated_at(borrow_index, location); - } - - (Reservation(_) | Activation(_, _) | Write(_), _) => { - // unique or mutable borrows are invalidated by writes. - // Reservations count as writes since we need to check - // that activating the borrow will be OK - // FIXME(bob_twinkles) is this actually the right thing to do? - this.emit_loan_invalidated_at(borrow_index, location); - } - } - Control::Continue - }, - ); - } - - /// Generates a new `loan_invalidated_at(L, B)` fact. - fn emit_loan_invalidated_at(&mut self, b: BorrowIndex, l: Location) { - let lidx = self.location_table.start_index(l); - self.all_facts.loan_invalidated_at.push((lidx, b)); - } - - fn check_activations(&mut self, location: Location) { - // Two-phase borrow support: For each activation that is newly - // generated at this statement, check if it interferes with - // another borrow. - for &borrow_index in self.borrow_set.activations_at_location(location) { - let borrow = &self.borrow_set[borrow_index]; - - // only mutable borrows should be 2-phase - assert!(match borrow.kind { - BorrowKind::Shared | BorrowKind::Fake => false, - BorrowKind::Mut { .. } => true, - }); - - self.access_place( - location, - borrow.borrowed_place, - (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)), - LocalMutationIsAllowed::No, - ); - - // We do not need to call `check_if_path_or_subpath_is_moved` - // again, as we already called it when we made the - // initial reservation. - } - } -} diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5787ea13e..43f48f579 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1,8 +1,8 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. #![allow(internal_features)] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] -#![cfg_attr(not(bootstrap), doc(rust_logo))] +#![feature(rustdoc_internals)] +#![doc(rust_logo)] #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(let_chains)] @@ -22,8 +22,7 @@ extern crate tracing; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::dominators::Dominators; -use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticMessage, SubdiagnosticMessage}; -use rustc_fluent_macro::fluent_messages; +use rustc_errors::{Diagnostic, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::{BitSet, ChunkedBitSet}; @@ -35,7 +34,7 @@ use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::traits::DefiningAnchor; -use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::UNUSED_MUT; use rustc_span::{Span, Symbol}; use rustc_target::abi::FieldIdx; @@ -43,6 +42,7 @@ use rustc_target::abi::FieldIdx; use smallvec::SmallVec; use std::cell::RefCell; use std::collections::BTreeMap; +use std::marker::PhantomData; use std::ops::Deref; use std::rc::Rc; @@ -65,19 +65,18 @@ use self::path_utils::*; pub mod borrow_set; mod borrowck_errors; -mod constraint_generation; mod constraints; mod dataflow; mod def_use; mod diagnostics; mod facts; -mod invalidation; mod location; mod member_constraints; mod nll; mod path_utils; mod place_ext; mod places_conflict; +mod polonius; mod prefixes; mod region_infer; mod renumber; @@ -98,19 +97,10 @@ use places_conflict::{places_conflict, PlaceConflictBias}; use region_infer::RegionInferenceContext; use renumber::RegionCtxt; -fluent_messages! { "../messages.ftl" } - -// FIXME(eddyb) perhaps move this somewhere more centrally. -#[derive(Debug)] -struct Upvar<'tcx> { - place: CapturedPlace<'tcx>, - - /// If true, the capture is behind a reference. - by_ref: bool, -} +rustc_fluent_macro::fluent_messages! { "../messages.ftl" } /// Associate some local constants with the `'tcx` lifetime -struct TyCtxtConsts<'tcx>(TyCtxt<'tcx>); +struct TyCtxtConsts<'tcx>(PhantomData<&'tcx ()>); impl<'tcx> TyCtxtConsts<'tcx> { const DEREF_PROJECTION: &'tcx [PlaceElem<'tcx>; 1] = &[ProjectionElem::Deref]; } @@ -135,7 +125,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> { return tcx.arena.alloc(result); } - let hir_owner = tcx.hir().local_def_id_to_hir_id(def).owner; + let hir_owner = tcx.local_def_id_to_hir_id(def).owner; let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)).build(); @@ -193,18 +183,6 @@ fn do_mir_borrowck<'tcx>( infcx.set_tainted_by_errors(e); errors.set_tainted_by_errors(e); } - let upvars: Vec<_> = tcx - .closure_captures(def) - .iter() - .map(|&captured_place| { - let capture = captured_place.info.capture_kind; - let by_ref = match capture { - ty::UpvarCapture::ByValue => false, - ty::UpvarCapture::ByRef(..) => true, - }; - Upvar { place: captured_place.clone(), by_ref } - }) - .collect(); // Replace all regions with fresh inference variables. This // requires first making our own copy of the MIR. This copy will @@ -216,21 +194,20 @@ fn do_mir_borrowck<'tcx>( nll::replace_regions_in_mir(&infcx, param_env, &mut body_owned, &mut promoted); let body = &body_owned; // no further changes - let location_table_owned = LocationTable::new(body); - let location_table = &location_table_owned; + let location_table = LocationTable::new(body); - let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true); + let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true); let promoted_move_data = promoted .iter_enumerated() - .map(|(idx, body)| (idx, MoveData::gather_moves(&body, tcx, param_env, |_| true))); + .map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, param_env, |_| true))); let mdpe = MoveDataParamEnv { move_data, param_env }; - let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body) + let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe) + .into_engine(tcx, body) .pass_name("borrowck") .iterate_to_fixpoint() - .into_results_cursor(&body); + .into_results_cursor(body); let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(def).is_fn_or_closure(); let borrow_set = @@ -249,24 +226,24 @@ fn do_mir_borrowck<'tcx>( free_regions, body, &promoted, - location_table, + &location_table, param_env, &mut flow_inits, &mdpe.move_data, &borrow_set, - &upvars, + tcx.closure_captures(def), consumer_options, ); // Dump MIR results into a file, if that is enabled. This let us // write unit-tests, as well as helping with debugging. - nll::dump_mir_results(&infcx, &body, ®ioncx, &opt_closure_req); + nll::dump_mir_results(&infcx, body, ®ioncx, &opt_closure_req); // We also have a `#[rustc_regions]` annotation that causes us to dump // information. nll::dump_annotation( &infcx, - &body, + body, ®ioncx, &opt_closure_req, &opaque_type_values, @@ -288,7 +265,7 @@ fn do_mir_borrowck<'tcx>( .into_engine(tcx, body) .pass_name("borrowck") .iterate_to_fixpoint(); - let flow_ever_inits = EverInitializedPlaces::new(tcx, body, &mdpe) + let flow_ever_inits = EverInitializedPlaces::new(body, &mdpe) .into_engine(tcx, body) .pass_name("borrowck") .iterate_to_fixpoint(); @@ -313,7 +290,7 @@ fn do_mir_borrowck<'tcx>( param_env, body: promoted_body, move_data: &move_data, - location_table, // no need to create a real one for the promoted, it is not used + location_table: &location_table, // no need to create a real one for the promoted, it is not used movable_coroutine, fn_self_span_reported: Default::default(), locals_are_invalidated_at_exit, @@ -324,7 +301,7 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - upvars: Vec::new(), + upvars: &[], local_names: IndexVec::from_elem(None, &promoted_body.local_decls), region_names: RefCell::default(), next_region_name: RefCell::new(1), @@ -354,7 +331,7 @@ fn do_mir_borrowck<'tcx>( param_env, body, move_data: &mdpe.move_data, - location_table, + location_table: &location_table, movable_coroutine, locals_are_invalidated_at_exit, fn_self_span_reported: Default::default(), @@ -365,7 +342,7 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - upvars, + upvars: tcx.closure_captures(def), local_names, region_names: RefCell::default(), next_region_name: RefCell::new(1), @@ -456,7 +433,7 @@ fn do_mir_borrowck<'tcx>( promoted, borrow_set, region_inference_context: regioncx, - location_table: polonius_input.as_ref().map(|_| location_table_owned), + location_table: polonius_input.as_ref().map(|_| location_table), input_facts: polonius_input, output_facts, })) @@ -584,7 +561,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> { borrow_set: Rc>, /// Information about upvars not necessarily preserved in types or MIR - upvars: Vec>, + upvars: &'tcx [&'tcx ty::CapturedPlace<'tcx>], /// Names of local (user) variables (extracted from `var_debug_info`). local_names: IndexVec>, @@ -1041,9 +1018,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { flow_state: &Flows<'cx, 'tcx>, ) -> bool { let mut error_reported = false; - let tcx = self.infcx.tcx; - let body = self.body; - let borrow_set = self.borrow_set.clone(); + let borrow_set = Rc::clone(&self.borrow_set); // Use polonius output if it has been enabled. let mut polonius_output; @@ -1060,8 +1035,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { each_borrow_involving_path( self, - tcx, - body, + self.infcx.tcx, + self.body, location, (sd, place_span.0), &borrow_set, @@ -1538,7 +1513,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if places_conflict::borrow_conflicts_with_place( self.infcx.tcx, - &self.body, + self.body, place, borrow.kind, root_place, @@ -2155,11 +2130,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { && !self.has_buffered_errors() { // rust-lang/rust#46908: In pure NLL mode this code path should be - // unreachable, but we use `delay_span_bug` because we can hit this when + // unreachable, but we use `span_delayed_bug` because we can hit this when // dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug` // enabled. We don't want to ICE for that case, as other errors will have // been emitted (#52262). - self.infcx.tcx.sess.delay_span_bug( + self.infcx.tcx.sess.span_delayed_bug( span, format!( "Accessing `{place:?}` with the kind `{kind:?}` shouldn't be possible", @@ -2193,7 +2168,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // If this is a mutate access to an immutable local variable with no projections // report the error as an illegal reassignment let init = &self.move_data.inits[init_index]; - let assigned_span = init.span(&self.body); + let assigned_span = init.span(self.body); self.report_illegal_reassignment(location, (place, span), assigned_span, place); } else { self.report_mutability_error(place, span, the_place_err, error_access, location) @@ -2294,7 +2269,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // unique path to the `&mut` hir::Mutability::Mut => { let mode = match self.is_upvar_field_projection(place) { - Some(field) if self.upvars[field.index()].by_ref => { + Some(field) + if self.upvars[field.index()].is_by_ref() => + { is_local_mutation_allowed } _ => LocalMutationIsAllowed::Yes, @@ -2342,7 +2319,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place={:?}, place_base={:?}", upvar, is_local_mutation_allowed, place, place_base ); - match (upvar.place.mutability, is_local_mutation_allowed) { + match (upvar.mutability, is_local_mutation_allowed) { ( Mutability::Not, LocalMutationIsAllowed::No @@ -2451,7 +2428,7 @@ mod error { pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) { if let None = self.tainted_by_errors { - self.tainted_by_errors = Some(self.tcx.sess.delay_span_bug( + self.tainted_by_errors = Some(self.tcx.sess.span_delayed_bug( t.span.clone_ignoring_labels(), "diagnostic buffered but not emitted", )) @@ -2525,8 +2502,8 @@ mod error { if !self.errors.buffered.is_empty() { self.errors.buffered.sort_by_key(|diag| diag.sort_span); - for mut diag in self.errors.buffered.drain(..) { - self.infcx.tcx.sess.diagnostic().emit_diagnostic(&mut diag); + for diag in self.errors.buffered.drain(..) { + self.infcx.tcx.sess.dcx().emit_diagnostic(diag); } } diff --git a/compiler/rustc_borrowck/src/location.rs b/compiler/rustc_borrowck/src/location.rs index 0e669abfd..6f0939316 100644 --- a/compiler/rustc_borrowck/src/location.rs +++ b/compiler/rustc_borrowck/src/location.rs @@ -20,6 +20,7 @@ pub struct LocationTable { } rustc_index::newtype_index! { + #[orderable] #[debug_format = "LocationIndex({})"] pub struct LocationIndex {} } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 0ea4401a8..6781c6a75 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -2,16 +2,17 @@ #![deny(rustc::diagnostic_outside_of_impl)] //! The entry point of the NLL borrow checker. +use polonius_engine::{Algorithm, Output}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::LocalDefId; use rustc_index::IndexSlice; use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere}; -use rustc_middle::mir::{ - Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted, - START_BLOCK, -}; +use rustc_middle::mir::{Body, ClosureOutlivesSubject, ClosureRegionRequirements, Promoted}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt}; +use rustc_mir_dataflow::impls::MaybeInitializedPlaces; +use rustc_mir_dataflow::move_paths::MoveData; +use rustc_mir_dataflow::ResultsCursor; use rustc_span::symbol::sym; use std::env; use std::io; @@ -19,25 +20,18 @@ use std::path::PathBuf; use std::rc::Rc; use std::str::FromStr; -use polonius_engine::{Algorithm, Output}; - -use rustc_mir_dataflow::impls::MaybeInitializedPlaces; -use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; -use rustc_mir_dataflow::ResultsCursor; - use crate::{ borrow_set::BorrowSet, - constraint_generation, consumers::ConsumerOptions, diagnostics::RegionErrors, facts::{AllFacts, AllFactsExt, RustcFacts}, - invalidation, location::LocationTable, + polonius, region_infer::{values::RegionValueElements, RegionInferenceContext}, renumber, type_check::{self, MirTypeckRegionConstraints, MirTypeckResults}, universal_regions::UniversalRegions, - BorrowckInferCtxt, Upvar, + BorrowckInferCtxt, }; pub type PoloniusOutput = Output; @@ -78,81 +72,6 @@ pub(crate) fn replace_regions_in_mir<'tcx>( universal_regions } -// This function populates an AllFacts instance with base facts related to -// MovePaths and needed for the move analysis. -fn populate_polonius_move_facts( - all_facts: &mut AllFacts, - move_data: &MoveData<'_>, - location_table: &LocationTable, - body: &Body<'_>, -) { - all_facts - .path_is_var - .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); - - for (child, move_path) in move_data.move_paths.iter_enumerated() { - if let Some(parent) = move_path.parent { - all_facts.child_path.push((child, parent)); - } - } - - let fn_entry_start = - location_table.start_index(Location { block: START_BLOCK, statement_index: 0 }); - - // initialized_at - for init in move_data.inits.iter() { - match init.location { - InitLocation::Statement(location) => { - let block_data = &body[location.block]; - let is_terminator = location.statement_index == block_data.statements.len(); - - if is_terminator && init.kind == InitKind::NonPanicPathOnly { - // We are at the terminator of an init that has a panic path, - // and where the init should not happen on panic - - for successor in block_data.terminator().successors() { - if body[successor].is_cleanup { - continue; - } - - // The initialization happened in (or rather, when arriving at) - // the successors, but not in the unwind block. - let first_statement = Location { block: successor, statement_index: 0 }; - all_facts - .path_assigned_at_base - .push((init.path, location_table.start_index(first_statement))); - } - } else { - // In all other cases, the initialization just happens at the - // midpoint, like any other effect. - all_facts - .path_assigned_at_base - .push((init.path, location_table.mid_index(location))); - } - } - // Arguments are initialized on function entry - InitLocation::Argument(local) => { - assert!(body.local_kind(local) == LocalKind::Arg); - all_facts.path_assigned_at_base.push((init.path, fn_entry_start)); - } - } - } - - for (local, path) in move_data.rev_lookup.iter_locals_enumerated() { - if body.local_kind(local) != LocalKind::Arg { - // Non-arguments start out deinitialised; we simulate this with an - // initial move: - all_facts.path_moved_at_base.push((path, fn_entry_start)); - } - } - - // moved_out_at - // deinitialisation is assumed to always happen! - all_facts - .path_moved_at_base - .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); -} - /// Computes the (non-lexical) regions from the input MIR. /// /// This may result in errors being reported. @@ -166,7 +85,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>( flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, - upvars: &[Upvar<'tcx>], + upvars: &[&ty::CapturedPlace<'tcx>], consumer_options: Option, ) -> NllOutput<'tcx> { let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); @@ -179,70 +98,26 @@ pub(crate) fn compute_regions<'cx, 'tcx>( let universal_regions = Rc::new(universal_regions); - let elements = &Rc::new(RegionValueElements::new(&body)); + let elements = &Rc::new(RegionValueElements::new(body)); // Run the MIR type-checker. - let MirTypeckResults { - constraints, - universal_region_relations, - opaque_type_values, - live_loans, - } = type_check::type_check( - infcx, - param_env, - body, - promoted, - &universal_regions, - location_table, - borrow_set, - &mut all_facts, - flow_inits, - move_data, - elements, - upvars, - polonius_input, - ); - - if let Some(all_facts) = &mut all_facts { - let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation"); - all_facts.universal_region.extend(universal_regions.universal_regions()); - populate_polonius_move_facts(all_facts, move_data, location_table, &body); - - // Emit universal regions facts, and their relations, for Polonius. - // - // 1: universal regions are modeled in Polonius as a pair: - // - the universal region vid itself. - // - a "placeholder loan" associated to this universal region. Since they don't exist in - // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index - // added to the existing number of loans, as if they succeeded them in the set. - // - let borrow_count = borrow_set.len(); - debug!( - "compute_regions: polonius placeholders, num_universals={}, borrow_count={}", - universal_regions.len(), - borrow_count + let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } = + type_check::type_check( + infcx, + param_env, + body, + promoted, + &universal_regions, + location_table, + borrow_set, + &mut all_facts, + flow_inits, + move_data, + elements, + upvars, + polonius_input, ); - for universal_region in universal_regions.universal_regions() { - let universal_region_idx = universal_region.index(); - let placeholder_loan_idx = borrow_count + universal_region_idx; - all_facts.placeholder.push((universal_region, placeholder_loan_idx.into())); - } - - // 2: the universal region relations `outlives` constraints are emitted as - // `known_placeholder_subset` facts. - for (fr1, fr2) in universal_region_relations.known_outlives() { - if fr1 != fr2 { - debug!( - "compute_regions: emitting polonius `known_placeholder_subset` \ - fr1={:?}, fr2={:?}", - fr1, fr2 - ); - all_facts.known_placeholder_subset.push((fr1, fr2)); - } - } - } - // Create the region inference context, taking ownership of the // region inference data that was contained in `infcx`, and the // base constraints generated by the type-check. @@ -250,7 +125,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>( let MirTypeckRegionConstraints { placeholder_indices, placeholder_index_to_region: _, - mut liveness_constraints, + liveness_constraints, outlives_constraints, member_constraints, universe_causes, @@ -258,13 +133,16 @@ pub(crate) fn compute_regions<'cx, 'tcx>( } = constraints; let placeholder_indices = Rc::new(placeholder_indices); - constraint_generation::generate_constraints( - infcx, - &mut liveness_constraints, + // If requested, emit legacy polonius facts. + polonius::emit_facts( &mut all_facts, + infcx.tcx, location_table, - &body, + body, borrow_set, + move_data, + &universal_regions, + &universal_region_relations, ); let mut regioncx = RegionInferenceContext::new( @@ -279,17 +157,12 @@ pub(crate) fn compute_regions<'cx, 'tcx>( type_tests, liveness_constraints, elements, - live_loans, ); - // Generate various additional constraints. - invalidation::generate_invalidates(infcx.tcx, &mut all_facts, location_table, body, borrow_set); - - let def_id = body.source.def_id(); - - // Dump facts if requested. + // If requested: dump NLL facts, and run legacy polonius analysis. let polonius_output = all_facts.as_ref().and_then(|all_facts| { if infcx.tcx.sess.opts.unstable_opts.nll_facts { + let def_id = body.source.def_id(); let def_path = infcx.tcx.def_path(def_id); let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir) .join(def_path.to_filename_friendly_no_crate()); @@ -302,7 +175,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>( let algorithm = Algorithm::from_str(&algorithm).unwrap(); debug!("compute_regions: using polonius algorithm {:?}", algorithm); let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis"); - Some(Rc::new(Output::compute(&all_facts, algorithm, false))) + Some(Rc::new(Output::compute(all_facts, algorithm, false))) } else { None } @@ -310,17 +183,17 @@ pub(crate) fn compute_regions<'cx, 'tcx>( // Solve the region constraints. let (closure_region_requirements, nll_errors) = - regioncx.solve(infcx, param_env, &body, polonius_output.clone()); + regioncx.solve(infcx, param_env, body, polonius_output.clone()); if !nll_errors.is_empty() { // Suppress unhelpful extra errors in `infer_opaque_types`. - infcx.set_tainted_by_errors(infcx.tcx.sess.delay_span_bug( + infcx.set_tainted_by_errors(infcx.tcx.sess.span_delayed_bug( body.span, "`compute_regions` tainted `infcx` with errors but did not emit any errors", )); } - let remapped_opaque_tys = regioncx.infer_opaque_types(&infcx, opaque_type_values); + let remapped_opaque_tys = regioncx.infer_opaque_types(infcx, opaque_type_values); NllOutput { regioncx, @@ -407,7 +280,7 @@ pub(super) fn dump_annotation<'tcx>( let def_span = tcx.def_span(body.source.def_id()); let mut err = if let Some(closure_region_requirements) = closure_region_requirements { - let mut err = tcx.sess.diagnostic().span_note_diag(def_span, "external requirements"); + let mut err = tcx.sess.dcx().struct_span_note(def_span, "external requirements"); regioncx.annotate(tcx, &mut err); @@ -426,7 +299,7 @@ pub(super) fn dump_annotation<'tcx>( err } else { - let mut err = tcx.sess.diagnostic().span_note_diag(def_span, "no external requirements"); + let mut err = tcx.sess.dcx().struct_span_note(def_span, "no external requirements"); regioncx.annotate(tcx, &mut err); err diff --git a/compiler/rustc_borrowck/src/path_utils.rs b/compiler/rustc_borrowck/src/path_utils.rs index 51e318f08..2d997dfad 100644 --- a/compiler/rustc_borrowck/src/path_utils.rs +++ b/compiler/rustc_borrowck/src/path_utils.rs @@ -4,7 +4,6 @@ use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; use crate::places_conflict; use crate::AccessDepth; use crate::BorrowIndex; -use crate::Upvar; use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::mir::BorrowKind; use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem}; @@ -150,7 +149,7 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool { /// of a closure type. pub(crate) fn is_upvar_field_projection<'tcx>( tcx: TyCtxt<'tcx>, - upvars: &[Upvar<'tcx>], + upvars: &[&rustc_middle::ty::CapturedPlace<'tcx>], place_ref: PlaceRef<'tcx>, body: &Body<'tcx>, ) -> Option { @@ -166,7 +165,7 @@ pub(crate) fn is_upvar_field_projection<'tcx>( Some((place_base, ProjectionElem::Field(field, _ty))) => { let base_ty = place_base.ty(body, tcx).ty; if (base_ty.is_closure() || base_ty.is_coroutine()) - && (!by_ref || upvars[field.index()].by_ref) + && (!by_ref || upvars[field.index()].is_by_ref()) { Some(field) } else { diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs new file mode 100644 index 000000000..232bd7418 --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -0,0 +1,424 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] +use rustc_data_structures::graph::dominators::Dominators; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{self, BasicBlock, Body, Location, NonDivergingIntrinsic, Place, Rvalue}; +use rustc_middle::mir::{BorrowKind, Mutability, Operand}; +use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; +use rustc_middle::mir::{Statement, StatementKind}; +use rustc_middle::ty::TyCtxt; + +use crate::{ + borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, path_utils::*, AccessDepth, + Activation, ArtificialField, BorrowIndex, Deep, LocalMutationIsAllowed, Read, ReadKind, + ReadOrWrite, Reservation, Shallow, Write, WriteKind, +}; + +/// Emit `loan_invalidated_at` facts. +pub(super) fn emit_loan_invalidations<'tcx>( + tcx: TyCtxt<'tcx>, + all_facts: &mut AllFacts, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, +) { + let dominators = body.basic_blocks.dominators(); + let mut visitor = + LoanInvalidationsGenerator { all_facts, borrow_set, tcx, location_table, body, dominators }; + visitor.visit_body(body); +} + +struct LoanInvalidationsGenerator<'cx, 'tcx> { + tcx: TyCtxt<'tcx>, + all_facts: &'cx mut AllFacts, + location_table: &'cx LocationTable, + body: &'cx Body<'tcx>, + dominators: &'cx Dominators, + borrow_set: &'cx BorrowSet<'tcx>, +} + +/// Visits the whole MIR and generates `invalidates()` facts. +/// Most of the code implementing this was stolen from `borrow_check/mod.rs`. +impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> { + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + self.check_activations(location); + + match &statement.kind { + StatementKind::Assign(box (lhs, rhs)) => { + self.consume_rvalue(location, rhs); + + self.mutate_place(location, *lhs, Shallow(None)); + } + StatementKind::FakeRead(box (_, _)) => { + // Only relevant for initialized/liveness/safety checks. + } + StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => { + self.consume_operand(location, op); + } + StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { + src, + dst, + count, + })) => { + self.consume_operand(location, src); + self.consume_operand(location, dst); + self.consume_operand(location, count); + } + // Only relevant for mir typeck + StatementKind::AscribeUserType(..) + // Only relevant for liveness and unsafeck + | StatementKind::PlaceMention(..) + // Doesn't have any language semantics + | StatementKind::Coverage(..) + // Does not actually affect borrowck + | StatementKind::StorageLive(..) => {} + StatementKind::StorageDead(local) => { + self.access_place( + location, + Place::from(*local), + (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), + LocalMutationIsAllowed::Yes, + ); + } + StatementKind::ConstEvalCounter + | StatementKind::Nop + | StatementKind::Retag { .. } + | StatementKind::Deinit(..) + | StatementKind::SetDiscriminant { .. } => { + bug!("Statement not allowed in this MIR phase") + } + } + + self.super_statement(statement, location); + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + self.check_activations(location); + + match &terminator.kind { + TerminatorKind::SwitchInt { discr, targets: _ } => { + self.consume_operand(location, discr); + } + TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => { + let write_kind = + if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop }; + self.access_place( + location, + *drop_place, + (AccessDepth::Drop, Write(write_kind)), + LocalMutationIsAllowed::Yes, + ); + } + TerminatorKind::Call { + func, + args, + destination, + target: _, + unwind: _, + call_source: _, + fn_span: _, + } => { + self.consume_operand(location, func); + for arg in args { + self.consume_operand(location, arg); + } + self.mutate_place(location, *destination, Deep); + } + TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { + self.consume_operand(location, cond); + use rustc_middle::mir::AssertKind; + if let AssertKind::BoundsCheck { len, index } = &**msg { + self.consume_operand(location, len); + self.consume_operand(location, index); + } + } + TerminatorKind::Yield { value, resume, resume_arg, drop: _ } => { + self.consume_operand(location, value); + + // Invalidate all borrows of local places + let borrow_set = self.borrow_set; + let resume = self.location_table.start_index(resume.start_location()); + for (i, data) in borrow_set.iter_enumerated() { + if borrow_of_local_data(data.borrowed_place) { + self.all_facts.loan_invalidated_at.push((resume, i)); + } + } + + self.mutate_place(location, *resume_arg, Deep); + } + TerminatorKind::UnwindResume + | TerminatorKind::Return + | TerminatorKind::CoroutineDrop => { + // Invalidate all borrows of local places + let borrow_set = self.borrow_set; + let start = self.location_table.start_index(location); + for (i, data) in borrow_set.iter_enumerated() { + if borrow_of_local_data(data.borrowed_place) { + self.all_facts.loan_invalidated_at.push((start, i)); + } + } + } + TerminatorKind::InlineAsm { + template: _, + operands, + options: _, + line_spans: _, + destination: _, + unwind: _, + } => { + for op in operands { + match op { + InlineAsmOperand::In { reg: _, value } => { + self.consume_operand(location, value); + } + InlineAsmOperand::Out { reg: _, late: _, place, .. } => { + if let &Some(place) = place { + self.mutate_place(location, place, Shallow(None)); + } + } + InlineAsmOperand::InOut { reg: _, late: _, in_value, out_place } => { + self.consume_operand(location, in_value); + if let &Some(out_place) = out_place { + self.mutate_place(location, out_place, Shallow(None)); + } + } + InlineAsmOperand::Const { value: _ } + | InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } + TerminatorKind::Goto { target: _ } + | TerminatorKind::UnwindTerminate(_) + | TerminatorKind::Unreachable + | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } + | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { + // no data used, thus irrelevant to borrowck + } + } + + self.super_terminator(terminator, location); + } +} + +impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> { + /// Simulates mutation of a place. + fn mutate_place(&mut self, location: Location, place: Place<'tcx>, kind: AccessDepth) { + self.access_place( + location, + place, + (kind, Write(WriteKind::Mutate)), + LocalMutationIsAllowed::ExceptUpvars, + ); + } + + /// Simulates consumption of an operand. + fn consume_operand(&mut self, location: Location, operand: &Operand<'tcx>) { + match *operand { + Operand::Copy(place) => { + self.access_place( + location, + place, + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + ); + } + Operand::Move(place) => { + self.access_place( + location, + place, + (Deep, Write(WriteKind::Move)), + LocalMutationIsAllowed::Yes, + ); + } + Operand::Constant(_) => {} + } + } + + // Simulates consumption of an rvalue + fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) { + match rvalue { + &Rvalue::Ref(_ /*rgn*/, bk, place) => { + let access_kind = match bk { + BorrowKind::Fake => { + (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) + } + BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), + BorrowKind::Mut { .. } => { + let wk = WriteKind::MutableBorrow(bk); + if allow_two_phase_borrow(bk) { + (Deep, Reservation(wk)) + } else { + (Deep, Write(wk)) + } + } + }; + + self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); + } + + &Rvalue::AddressOf(mutability, place) => { + let access_kind = match mutability { + Mutability::Mut => ( + Deep, + Write(WriteKind::MutableBorrow(BorrowKind::Mut { + kind: mir::MutBorrowKind::Default, + })), + ), + Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + }; + + self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); + } + + Rvalue::ThreadLocalRef(_) => {} + + Rvalue::Use(operand) + | Rvalue::Repeat(operand, _) + | Rvalue::UnaryOp(_ /*un_op*/, operand) + | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) + | Rvalue::ShallowInitBox(operand, _ /*ty*/) => self.consume_operand(location, operand), + + &Rvalue::CopyForDeref(place) => { + let op = &Operand::Copy(place); + self.consume_operand(location, op); + } + + &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { + let af = match rvalue { + Rvalue::Len(..) => Some(ArtificialField::ArrayLength), + Rvalue::Discriminant(..) => None, + _ => unreachable!(), + }; + self.access_place( + location, + place, + (Shallow(af), Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + ); + } + + Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) + | Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => { + self.consume_operand(location, operand1); + self.consume_operand(location, operand2); + } + + Rvalue::NullaryOp(_op, _ty) => {} + + Rvalue::Aggregate(_, operands) => { + for operand in operands { + self.consume_operand(location, operand); + } + } + } + } + + /// Simulates an access to a place. + fn access_place( + &mut self, + location: Location, + place: Place<'tcx>, + kind: (AccessDepth, ReadOrWrite), + _is_local_mutation_allowed: LocalMutationIsAllowed, + ) { + let (sd, rw) = kind; + // note: not doing check_access_permissions checks because they don't generate invalidates + self.check_access_for_conflict(location, place, sd, rw); + } + + fn check_access_for_conflict( + &mut self, + location: Location, + place: Place<'tcx>, + sd: AccessDepth, + rw: ReadOrWrite, + ) { + debug!( + "check_access_for_conflict(location={:?}, place={:?}, sd={:?}, rw={:?})", + location, place, sd, rw, + ); + each_borrow_involving_path( + self, + self.tcx, + self.body, + location, + (sd, place), + self.borrow_set, + |_| true, + |this, borrow_index, borrow| { + match (rw, borrow.kind) { + // Obviously an activation is compatible with its own + // reservation (or even prior activating uses of same + // borrow); so don't check if they interfere. + // + // NOTE: *reservations* do conflict with themselves; + // thus aren't injecting unsoundness w/ this check.) + (Activation(_, activating), _) if activating == borrow_index => { + // Activating a borrow doesn't generate any invalidations, since we + // have already taken the reservation + } + + (Read(_), BorrowKind::Fake | BorrowKind::Shared) + | (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => { + // Reads don't invalidate shared or shallow borrows + } + + (Read(_), BorrowKind::Mut { .. }) => { + // Reading from mere reservations of mutable-borrows is OK. + if !is_active(this.dominators, borrow, location) { + // If the borrow isn't active yet, reads don't invalidate it + assert!(allow_two_phase_borrow(borrow.kind)); + return Control::Continue; + } + + // Unique and mutable borrows are invalidated by reads from any + // involved path + this.emit_loan_invalidated_at(borrow_index, location); + } + + (Reservation(_) | Activation(_, _) | Write(_), _) => { + // unique or mutable borrows are invalidated by writes. + // Reservations count as writes since we need to check + // that activating the borrow will be OK + // FIXME(bob_twinkles) is this actually the right thing to do? + this.emit_loan_invalidated_at(borrow_index, location); + } + } + Control::Continue + }, + ); + } + + /// Generates a new `loan_invalidated_at(L, B)` fact. + fn emit_loan_invalidated_at(&mut self, b: BorrowIndex, l: Location) { + let lidx = self.location_table.start_index(l); + self.all_facts.loan_invalidated_at.push((lidx, b)); + } + + fn check_activations(&mut self, location: Location) { + // Two-phase borrow support: For each activation that is newly + // generated at this statement, check if it interferes with + // another borrow. + for &borrow_index in self.borrow_set.activations_at_location(location) { + let borrow = &self.borrow_set[borrow_index]; + + // only mutable borrows should be 2-phase + assert!(match borrow.kind { + BorrowKind::Shared | BorrowKind::Fake => false, + BorrowKind::Mut { .. } => true, + }); + + self.access_place( + location, + borrow.borrowed_place, + (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index)), + LocalMutationIsAllowed::No, + ); + + // We do not need to call `check_if_path_or_subpath_is_moved` + // again, as we already called it when we made the + // initial reservation. + } + } +} diff --git a/compiler/rustc_borrowck/src/polonius/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/loan_kills.rs new file mode 100644 index 000000000..5df943837 --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/loan_kills.rs @@ -0,0 +1,147 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{ + Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, + Terminator, TerminatorKind, +}; +use rustc_middle::ty::TyCtxt; + +use crate::{borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, places_conflict}; + +/// Emit `loan_killed_at` and `cfg_edge` facts at the same time. +pub(super) fn emit_loan_kills<'tcx>( + tcx: TyCtxt<'tcx>, + all_facts: &mut AllFacts, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, +) { + let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, all_facts, body }; + for (bb, data) in body.basic_blocks.iter_enumerated() { + visitor.visit_basic_block_data(bb, data); + } +} + +struct LoanKillsGenerator<'cx, 'tcx> { + tcx: TyCtxt<'tcx>, + all_facts: &'cx mut AllFacts, + location_table: &'cx LocationTable, + borrow_set: &'cx BorrowSet<'tcx>, + body: &'cx Body<'tcx>, +} + +impl<'cx, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'cx, 'tcx> { + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + // Also record CFG facts here. + self.all_facts.cfg_edge.push(( + self.location_table.start_index(location), + self.location_table.mid_index(location), + )); + + self.all_facts.cfg_edge.push(( + self.location_table.mid_index(location), + self.location_table.start_index(location.successor_within_block()), + )); + + // If there are borrows on this now dead local, we need to record them as `killed`. + if let StatementKind::StorageDead(local) = statement.kind { + self.record_killed_borrows_for_local(local, location); + } + + self.super_statement(statement, location); + } + + fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + // When we see `X = ...`, then kill borrows of + // `(*X).foo` and so forth. + self.record_killed_borrows_for_place(*place, location); + self.super_assign(place, rvalue, location); + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + // Also record CFG facts here. + self.all_facts.cfg_edge.push(( + self.location_table.start_index(location), + self.location_table.mid_index(location), + )); + + let successor_blocks = terminator.successors(); + self.all_facts.cfg_edge.reserve(successor_blocks.size_hint().0); + for successor_block in successor_blocks { + self.all_facts.cfg_edge.push(( + self.location_table.mid_index(location), + self.location_table.start_index(successor_block.start_location()), + )); + } + + // A `Call` terminator's return value can be a local which has borrows, + // so we need to record those as `killed` as well. + if let TerminatorKind::Call { destination, .. } = terminator.kind { + self.record_killed_borrows_for_place(destination, location); + } + + self.super_terminator(terminator, location); + } +} + +impl<'tcx> LoanKillsGenerator<'_, 'tcx> { + /// Records the borrows on the specified place as `killed`. For example, when assigning to a + /// local, or on a call's return destination. + fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) { + // Depending on the `Place` we're killing: + // - if it's a local, or a single deref of a local, + // we kill all the borrows on the local. + // - if it's a deeper projection, we have to filter which + // of the borrows are killed: the ones whose `borrowed_place` + // conflicts with the `place`. + match place.as_ref() { + PlaceRef { local, projection: &[] } + | PlaceRef { local, projection: &[ProjectionElem::Deref] } => { + debug!( + "Recording `killed` facts for borrows of local={:?} at location={:?}", + local, location + ); + + self.record_killed_borrows_for_local(local, location); + } + + PlaceRef { local, projection: &[.., _] } => { + // Kill conflicting borrows of the innermost local. + debug!( + "Recording `killed` facts for borrows of \ + innermost projected local={:?} at location={:?}", + local, location + ); + + if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) { + for &borrow_index in borrow_indices { + let places_conflict = places_conflict::places_conflict( + self.tcx, + self.body, + self.borrow_set[borrow_index].borrowed_place, + place, + places_conflict::PlaceConflictBias::NoOverlap, + ); + + if places_conflict { + let location_index = self.location_table.mid_index(location); + self.all_facts.loan_killed_at.push((borrow_index, location_index)); + } + } + } + } + } + } + + /// Records the borrows on the specified local as `killed`. + fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) { + if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) { + let location_index = self.location_table.mid_index(location); + self.all_facts.loan_killed_at.reserve(borrow_indices.len()); + for &borrow_index in borrow_indices { + self.all_facts.loan_killed_at.push((borrow_index, location_index)); + } + } + } +} diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs new file mode 100644 index 000000000..40126d50d --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -0,0 +1,188 @@ +//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation. +//! +//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature +//! parity. + +use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK}; +use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; + +use crate::borrow_set::BorrowSet; +use crate::facts::AllFacts; +use crate::location::LocationTable; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::universal_regions::UniversalRegions; + +mod loan_invalidations; +mod loan_kills; + +/// When requested, emit most of the facts needed by polonius: +/// - moves and assignments +/// - universal regions and their relations +/// - CFG points and edges +/// - loan kills +/// - loan invalidations +/// +/// The rest of the facts are emitted during typeck and liveness. +pub(crate) fn emit_facts<'tcx>( + all_facts: &mut Option, + tcx: TyCtxt<'tcx>, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, + move_data: &MoveData<'_>, + universal_regions: &UniversalRegions<'_>, + universal_region_relations: &UniversalRegionRelations<'_>, +) { + let Some(all_facts) = all_facts else { + // We don't do anything if there are no facts to fill. + return; + }; + let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); + emit_move_facts(all_facts, move_data, location_table, body); + emit_universal_region_facts( + all_facts, + borrow_set, + &universal_regions, + &universal_region_relations, + ); + emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set); + emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set); +} + +/// Emit facts needed for move/init analysis: moves and assignments. +fn emit_move_facts( + all_facts: &mut AllFacts, + move_data: &MoveData<'_>, + location_table: &LocationTable, + body: &Body<'_>, +) { + all_facts + .path_is_var + .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); + + for (child, move_path) in move_data.move_paths.iter_enumerated() { + if let Some(parent) = move_path.parent { + all_facts.child_path.push((child, parent)); + } + } + + let fn_entry_start = + location_table.start_index(Location { block: START_BLOCK, statement_index: 0 }); + + // initialized_at + for init in move_data.inits.iter() { + match init.location { + InitLocation::Statement(location) => { + let block_data = &body[location.block]; + let is_terminator = location.statement_index == block_data.statements.len(); + + if is_terminator && init.kind == InitKind::NonPanicPathOnly { + // We are at the terminator of an init that has a panic path, + // and where the init should not happen on panic + + for successor in block_data.terminator().successors() { + if body[successor].is_cleanup { + continue; + } + + // The initialization happened in (or rather, when arriving at) + // the successors, but not in the unwind block. + let first_statement = Location { block: successor, statement_index: 0 }; + all_facts + .path_assigned_at_base + .push((init.path, location_table.start_index(first_statement))); + } + } else { + // In all other cases, the initialization just happens at the + // midpoint, like any other effect. + all_facts + .path_assigned_at_base + .push((init.path, location_table.mid_index(location))); + } + } + // Arguments are initialized on function entry + InitLocation::Argument(local) => { + assert!(body.local_kind(local) == LocalKind::Arg); + all_facts.path_assigned_at_base.push((init.path, fn_entry_start)); + } + } + } + + for (local, path) in move_data.rev_lookup.iter_locals_enumerated() { + if body.local_kind(local) != LocalKind::Arg { + // Non-arguments start out deinitialised; we simulate this with an + // initial move: + all_facts.path_moved_at_base.push((path, fn_entry_start)); + } + } + + // moved_out_at + // deinitialisation is assumed to always happen! + all_facts + .path_moved_at_base + .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source)))); +} + +/// Emit universal regions facts, and their relations. +fn emit_universal_region_facts( + all_facts: &mut AllFacts, + borrow_set: &BorrowSet<'_>, + universal_regions: &UniversalRegions<'_>, + universal_region_relations: &UniversalRegionRelations<'_>, +) { + // 1: universal regions are modeled in Polonius as a pair: + // - the universal region vid itself. + // - a "placeholder loan" associated to this universal region. Since they don't exist in + // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index + // added to the existing number of loans, as if they succeeded them in the set. + // + all_facts.universal_region.extend(universal_regions.universal_regions()); + let borrow_count = borrow_set.len(); + debug!( + "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}", + universal_regions.len(), + borrow_count + ); + + for universal_region in universal_regions.universal_regions() { + let universal_region_idx = universal_region.index(); + let placeholder_loan_idx = borrow_count + universal_region_idx; + all_facts.placeholder.push((universal_region, placeholder_loan_idx.into())); + } + + // 2: the universal region relations `outlives` constraints are emitted as + // `known_placeholder_subset` facts. + for (fr1, fr2) in universal_region_relations.known_outlives() { + if fr1 != fr2 { + debug!( + "emit_universal_region_facts: emitting polonius `known_placeholder_subset` \ + fr1={:?}, fr2={:?}", + fr1, fr2 + ); + all_facts.known_placeholder_subset.push((fr1, fr2)); + } + } +} + +/// Emit facts about loan invalidations. +fn emit_loan_invalidations_facts<'tcx>( + all_facts: &mut AllFacts, + tcx: TyCtxt<'tcx>, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, +) { + loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set); +} + +/// Emit facts about CFG points and edges, as well as locations where loans are killed. +fn emit_cfg_and_loan_kills_facts<'tcx>( + all_facts: &mut AllFacts, + tcx: TyCtxt<'tcx>, + location_table: &LocationTable, + body: &Body<'tcx>, + borrow_set: &BorrowSet<'tcx>, +) { + loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set); +} diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index 4d620ac9d..cfbb2766c 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -67,7 +67,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { with_msg: &mut dyn FnMut(&str) -> io::Result<()>, ) -> io::Result<()> { for region in self.definitions.indices() { - let value = self.liveness_constraints.region_value_str(region); + let value = self.liveness_constraints.pretty_print_live_points(region); if value != "{}" { with_msg(&format!("{region:?} live at {value}"))?; } diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index a0cf22e93..408c8390e 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -8,7 +8,6 @@ use std::borrow::Cow; use std::io::{self, Write}; use super::*; -use crate::constraints::OutlivesConstraint; use rustc_graphviz as dot; impl<'tcx> RegionInferenceContext<'tcx> { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b1f91a056..948221e94 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -7,7 +7,6 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::scc::Sccs; use rustc_errors::Diagnostic; use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_index::bit_set::SparseBitMatrix; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::outlives::test_type_match; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; @@ -31,8 +30,8 @@ use crate::{ nll::PoloniusOutput, region_infer::reverse_sccs::ReverseSccGraph, region_infer::values::{ - LivenessValues, PlaceholderIndices, PointIndex, RegionElement, RegionValueElements, - RegionValues, ToElementIndex, + LivenessValues, PlaceholderIndices, RegionElement, RegionValueElements, RegionValues, + ToElementIndex, }, type_check::{free_region_relations::UniversalRegionRelations, Locations}, universal_regions::UniversalRegions, @@ -59,7 +58,7 @@ pub struct RegionInferenceContext<'tcx> { /// 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: LivenessValues, /// The outlives constraints computed by the type-check. constraints: Frozen>, @@ -120,9 +119,6 @@ pub struct RegionInferenceContext<'tcx> { /// Information about how the universally quantified regions in /// scope on this function relate to one another. universal_region_relations: Frozen>, - - /// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`. - live_loans: SparseBitMatrix, } /// Each time that `apply_member_constraint` is successful, it appends @@ -151,6 +147,7 @@ pub(crate) struct AppliedMemberConstraint { pub(crate) member_constraint_index: NllMemberConstraintIndex, } +#[derive(Debug)] pub(crate) struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential /// variable? etc. (See the `NllRegionVariableOrigin` for more @@ -332,9 +329,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { member_constraints_in: MemberConstraintSet<'tcx, RegionVid>, universe_causes: FxIndexMap>, type_tests: Vec>, - liveness_constraints: LivenessValues, + liveness_constraints: LivenessValues, elements: &Rc, - live_loans: SparseBitMatrix, ) -> Self { debug!("universal_regions: {:#?}", universal_regions); debug!("outlives constraints: {:#?}", outlives_constraints); @@ -359,7 +355,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut scc_values = RegionValues::new(elements, universal_regions.len(), &placeholder_indices); - for region in liveness_constraints.rows() { + for region in liveness_constraints.regions() { let scc = constraint_sccs.scc(region); scc_values.merge_liveness(scc, region, &liveness_constraints); } @@ -388,7 +384,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { type_tests, universal_regions, universal_region_relations, - live_loans, }; result.init_free_and_bound_regions(); @@ -679,13 +674,10 @@ 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, - param_env, - body, - outlives_requirements.as_mut(), - &mut errors_buffer, - ); + self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer); + + debug!(?errors_buffer); + debug!(?outlives_requirements); // In Polonius mode, the errors about missing universal region relations are in the output // and need to be emitted or propagated. Otherwise, we need to check whether the @@ -700,10 +692,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_universal_regions(outlives_requirements.as_mut(), &mut errors_buffer); } + debug!(?errors_buffer); + if errors_buffer.is_empty() { self.check_member_constraints(infcx, &mut errors_buffer); } + debug!(?errors_buffer); + let outlives_requirements = outlives_requirements.unwrap_or_default(); if outlives_requirements.is_empty() { @@ -936,7 +932,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn check_type_tests( &self, infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, mut propagated_outlives_requirements: Option<&mut Vec>>, errors_buffer: &mut RegionErrors<'tcx>, @@ -954,7 +949,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { let generic_ty = type_test.generic_kind.to_ty(tcx); if self.eval_verify_bound( infcx, - param_env, generic_ty, type_test.lower_bound, &type_test.verify_bound, @@ -965,7 +959,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { if self.try_promote_type_test( infcx, - param_env, body, type_test, propagated_outlives_requirements, @@ -1023,7 +1016,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn try_promote_type_test( &self, infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, type_test: &TypeTest<'tcx>, propagated_outlives_requirements: &mut Vec>, @@ -1085,7 +1077,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // where `ur` is a local bound -- we are sometimes in a // position to prove things that our caller cannot. See // #53570 for an example. - if self.eval_verify_bound(infcx, param_env, generic_ty, ur, &type_test.verify_bound) { + if self.eval_verify_bound(infcx, generic_ty, ur, &type_test.verify_bound) { continue; } @@ -1268,7 +1260,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn eval_verify_bound( &self, infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, verify_bound: &VerifyBound<'tcx>, @@ -1277,7 +1268,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { match verify_bound { VerifyBound::IfEq(verify_if_eq_b) => { - self.eval_if_eq(infcx, param_env, generic_ty, lower_bound, *verify_if_eq_b) + self.eval_if_eq(infcx, generic_ty, lower_bound, *verify_if_eq_b) } VerifyBound::IsEmpty => { @@ -1291,11 +1282,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { } VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { - self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound) + self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) }), VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| { - self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound) + self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) }), } } @@ -1303,19 +1294,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn eval_if_eq( &self, infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, verify_if_eq_b: ty::Binder<'tcx, VerifyIfEq<'tcx>>, ) -> bool { let generic_ty = self.normalize_to_scc_representatives(infcx.tcx, generic_ty); let verify_if_eq_b = self.normalize_to_scc_representatives(infcx.tcx, verify_if_eq_b); - match test_type_match::extract_verify_if_eq( - infcx.tcx, - param_env, - &verify_if_eq_b, - generic_ty, - ) { + 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) @@ -1457,6 +1442,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer: &mut RegionErrors<'tcx>, ) { for (fr, fr_definition) in self.definitions.iter_enumerated() { + debug!(?fr, ?fr_definition); match fr_definition.origin { NllRegionVariableOrigin::FreeRegion => { // Go through each of the universal regions `fr` and check that @@ -1963,15 +1949,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { None } - /// Finds some region R such that `fr1: R` and `R` is live at `elem`. + /// 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, elem: Location) -> RegionVid { + pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid { trace!(scc = ?self.constraint_sccs.scc(fr1)); trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]); self.find_constraint_paths_between_regions(fr1, |r| { - // First look for some `r` such that `fr1: r` and `r` is live at `elem` - trace!(?r, liveness_constraints=?self.liveness_constraints.region_value_str(r)); - self.liveness_constraints.contains(r, elem) + // First look for some `r` such that `fr1: r` and `r` is live at `location` + trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r)); + self.liveness_constraints.is_live_at(r, location) }) .or_else(|| { // If we fail to find that, we may find some `r` such that @@ -2316,7 +2302,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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.live_loans.contains(point, loan_idx) + self.liveness_constraints.is_loan_live_at(loan_idx, point) } } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index fb0e5811c..3764e4c40 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -36,7 +36,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// call `infer_opaque_definition_from_instantiation` to get the inferred /// type of `_Return<'_a>`. `infer_opaque_definition_from_instantiation` /// compares lifetimes directly, so we need to map the inference variables - /// back to concrete lifetimes: `'static`, `ReEarlyBound` or `ReFree`. + /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`. /// /// First we map all the lifetimes in the concrete type to an equal /// universal region that occurs in the concrete type's args, in this case @@ -315,7 +315,7 @@ fn check_opaque_type_well_formed<'tcx>( parent_def_id = tcx.local_parent(parent_def_id); } - // FIXME(-Ztrait-solver=next): We probably should use `DefiningAnchor::Error` + // FIXME(-Znext-solver): We probably should use `DefiningAnchor::Error` // and prepopulate this `InferCtxt` with known opaque values, rather than // using the `Bind` anchor here. For now it's fine. let infcx = tcx @@ -386,7 +386,7 @@ fn check_opaque_type_parameter_valid( let arg_is_param = match arg.unpack() { GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), GenericArgKind::Lifetime(lt) if is_ty_alias => { - matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)) + matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_)) } // FIXME(#113916): we can't currently check for unique lifetime params, // see that issue for more. We will also have to ignore unused lifetime diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 38452df32..dc3ee849d 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -11,6 +11,8 @@ use rustc_middle::ty::{self, RegionVid}; use std::fmt::Debug; use std::rc::Rc; +use crate::dataflow::BorrowIndex; + /// Maps between a `Location` and a `PointIndex` (and vice versa). pub(crate) struct RegionValueElements { /// For each basic block, how many points are contained within? @@ -90,6 +92,7 @@ impl RegionValueElements { rustc_index::newtype_index! { /// A single integer representing a `Location` in the MIR control-flow /// graph. Constructed efficiently from `RegionValueElements`. + #[orderable] #[debug_format = "PointIndex({})"] pub struct PointIndex {} } @@ -116,71 +119,132 @@ pub(crate) enum RegionElement { PlaceholderRegion(ty::PlaceholderRegion), } -/// When we initially compute liveness, we use an interval matrix storing -/// liveness ranges for each region-vid. -pub(crate) struct LivenessValues { +/// Records the CFG locations where each region is live. When we initially compute liveness, we use +/// an interval matrix storing liveness ranges for each region-vid. +pub(crate) struct LivenessValues { + /// The map from locations to points. elements: Rc, - points: SparseIntervalMatrix, + + /// For each region: the points where it is live. + points: SparseIntervalMatrix, + + /// When using `-Zpolonius=next`, for each point: the loans flowing into the live regions at + /// that point. + pub(crate) loans: Option, } -impl LivenessValues { - /// Creates a new set of "region values" that tracks causal information. - /// Each of the regions in num_region_variables will be initialized with an - /// empty set of points and no causal information. +/// Data used to compute the loans that are live at a given point in the CFG, when using +/// `-Zpolonius=next`. +pub(crate) struct LiveLoans { + /// The set of loans that flow into a given region. When individual regions are marked as live + /// in the CFG, these inflowing loans are recorded as live. + pub(crate) inflowing_loans: SparseBitMatrix, + + /// The set of loans that are live at a given point in the CFG. + pub(crate) live_loans: SparseBitMatrix, +} + +impl LiveLoans { + pub(crate) fn new(num_loans: usize) -> Self { + LiveLoans { + live_loans: SparseBitMatrix::new(num_loans), + inflowing_loans: SparseBitMatrix::new(num_loans), + } + } +} + +impl LivenessValues { + /// Create an empty map of regions to locations where they're live. pub(crate) fn new(elements: Rc) -> Self { - Self { points: SparseIntervalMatrix::new(elements.num_points), elements } + LivenessValues { + points: SparseIntervalMatrix::new(elements.num_points), + elements, + loans: None, + } } /// Iterate through each region that has a value in this set. - pub(crate) fn rows(&self) -> impl Iterator { + pub(crate) fn regions(&self) -> impl Iterator { self.points.rows() } - /// Adds the given element to the value for the given region. Returns whether - /// the element is newly added (i.e., was not already present). - pub(crate) fn add_element(&mut self, row: N, location: Location) -> bool { - debug!("LivenessValues::add(r={:?}, location={:?})", row, location); - let index = self.elements.point_from_location(location); - self.points.insert(row, index) + /// Records `region` as being live at the given `location`. + pub(crate) fn add_location(&mut self, region: RegionVid, location: Location) { + debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location); + let point = self.elements.point_from_location(location); + self.points.insert(region, point); + + // When available, record the loans flowing into this region as live at the given point. + if let Some(loans) = self.loans.as_mut() { + if let Some(inflowing) = loans.inflowing_loans.row(region) { + loans.live_loans.union_row(point, inflowing); + } + } } - /// Adds all the elements in the given bit array into the given - /// region. Returns whether any of them are newly added. - pub(crate) fn add_elements(&mut self, row: N, locations: &IntervalSet) -> bool { - debug!("LivenessValues::add_elements(row={:?}, locations={:?})", row, locations); - self.points.union_row(row, locations) + /// Records `region` as being live at all the given `points`. + pub(crate) fn add_points(&mut self, region: RegionVid, points: &IntervalSet) { + debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points); + self.points.union_row(region, points); + + // When available, record the loans flowing into this region as live at the given points. + if let Some(loans) = self.loans.as_mut() { + if let Some(inflowing) = loans.inflowing_loans.row(region) { + if !inflowing.is_empty() { + for point in points.iter() { + loans.live_loans.union_row(point, inflowing); + } + } + } + } } - /// Adds all the control-flow points to the values for `r`. - pub(crate) fn add_all_points(&mut self, row: N) { - self.points.insert_all_into_row(row); + /// Records `region` as being live at all the control-flow points. + pub(crate) fn add_all_points(&mut self, region: RegionVid) { + self.points.insert_all_into_row(region); } - /// Returns `true` if the region `r` contains the given element. - pub(crate) fn contains(&self, row: N, location: Location) -> bool { - let index = self.elements.point_from_location(location); - self.points.row(row).is_some_and(|r| r.contains(index)) + /// Returns whether `region` is marked live at the given `location`. + pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool { + let point = self.elements.point_from_location(location); + self.points.row(region).is_some_and(|r| r.contains(point)) + } + + /// Returns whether `region` is marked live at any location. + pub(crate) fn is_live_anywhere(&self, region: RegionVid) -> bool { + self.live_points(region).next().is_some() } - /// Returns an iterator of all the elements contained by the region `r` - pub(crate) fn get_elements(&self, row: N) -> impl Iterator + '_ { + /// Returns an iterator of all the points where `region` is live. + fn live_points(&self, region: RegionVid) -> impl Iterator + '_ { self.points - .row(row) + .row(region) .into_iter() .flat_map(|set| set.iter()) - .take_while(move |&p| self.elements.point_in_range(p)) - .map(move |p| self.elements.to_location(p)) + .take_while(|&p| self.elements.point_in_range(p)) } - /// Returns a "pretty" string value of the region. Meant for debugging. - pub(crate) fn region_value_str(&self, r: N) -> String { - region_value_str(self.get_elements(r).map(RegionElement::Location)) + /// For debugging purposes, returns a pretty-printed string of the points where the `region` is + /// live. + pub(crate) fn pretty_print_live_points(&self, region: RegionVid) -> String { + pretty_print_region_elements( + self.live_points(region).map(|p| RegionElement::Location(self.elements.to_location(p))), + ) } #[inline] pub(crate) fn point_from_location(&self, location: Location) -> PointIndex { self.elements.point_from_location(location) } + + /// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`. + pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool { + self.loans + .as_ref() + .expect("Accessing live loans requires `-Zpolonius=next`") + .live_loans + .contains(point, loan_idx) + } } /// Maps from `ty::PlaceholderRegion` values that are used in the rest of @@ -307,7 +371,7 @@ impl RegionValues { /// `self[to] |= values[from]`, essentially: that is, take all the /// elements for the region `from` from `values` and add them to /// the region `to` in `self`. - pub(crate) fn merge_liveness(&mut self, to: N, from: M, values: &LivenessValues) { + pub(crate) fn merge_liveness(&mut self, to: N, from: RegionVid, values: &LivenessValues) { if let Some(set) = values.points.row(from) { self.points.union_row(to, set); } @@ -376,7 +440,7 @@ impl RegionValues { /// Returns a "pretty" string value of the region. Meant for debugging. pub(crate) fn region_value_str(&self, r: N) -> String { - region_value_str(self.elements_contained_in(r)) + pretty_print_region_elements(self.elements_contained_in(r)) } } @@ -420,11 +484,12 @@ impl ToElementIndex for ty::PlaceholderRegion { } } -pub(crate) fn location_set_str( +/// For debugging purposes, returns a pretty-printed string of the given points. +pub(crate) fn pretty_print_points( elements: &RegionValueElements, points: impl IntoIterator, ) -> String { - region_value_str( + pretty_print_region_elements( points .into_iter() .take_while(|&p| elements.point_in_range(p)) @@ -433,7 +498,8 @@ pub(crate) fn location_set_str( ) } -fn region_value_str(elements: impl IntoIterator) -> String { +/// For debugging purposes, returns a pretty-printed string of the given region elements. +fn pretty_print_region_elements(elements: impl IntoIterator) -> String { let mut result = String::new(); result.push('{'); diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index ec0131c53..a31d39e14 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -28,6 +28,9 @@ pub fn renumber_mir<'tcx>( renumberer.visit_body(body); } +// FIXME(@lcnr): A lot of these variants overlap and it seems like +// this type is only used to decide which region should be used +// as representative. This should be cleaned up. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub(crate) enum RegionCtxt { Location(Location), diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index c84256f8f..4a76d877a 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -7,7 +7,7 @@ use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; -use rustc_middle::ty::{self, RegionVid, Ty}; +use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; @@ -303,7 +303,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { Locations::All(span), span, ConstraintCategory::Internal, - &mut self.constraints, + self.constraints, ) .convert_all(data); } @@ -321,6 +321,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; debug!(?bounds, ?constraints); + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders()); self.add_outlives_bounds(bounds); constraints } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index d053d0a4b..8e141bb38 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -7,7 +7,7 @@ //! `RETURN_PLACE` the MIR arguments) are always fully normalized (and //! contain revealed `impl Trait` values). -use rustc_infer::infer::LateBoundRegionConversionTime; +use rustc_infer::infer::BoundRegionConversionTime; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -35,7 +35,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig); let user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars( body.span, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, user_provided_sig, ); @@ -77,7 +77,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if argument_index + 1 >= body.local_decls.len() { self.tcx() .sess - .delay_span_bug(body.span, "found more normalized_input_ty than local_decls"); + .span_delayed_bug(body.span, "found more normalized_input_ty than local_decls"); break; } @@ -101,10 +101,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); // We will not have a universal_regions.yield_ty if we yield (by accident) - // outside of a coroutine and return an `impl Trait`, so emit a delay_span_bug + // outside of a coroutine and return an `impl Trait`, so emit a span_delayed_bug // because we don't want to panic in an assert here if we've already got errors. if body.yield_ty().is_some() != universal_regions.yield_ty.is_some() { - self.tcx().sess.delay_span_bug( + self.tcx().sess.span_delayed_bug( body.span, format!( "Expected body to have yield_ty ({:?}) iff we have a UR yield_ty ({:?})", diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs index a9ca94567..7433c94a0 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs @@ -80,7 +80,7 @@ impl LocalUseMap { live_locals.iter().for_each(|&local| locals_with_use_data[local] = true); LocalUseMapBuild { local_use_map: &mut local_use_map, elements, locals_with_use_data } - .visit_body(&body); + .visit_body(body); local_use_map } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index f1ad0ca55..dc4695fd2 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -1,7 +1,9 @@ use itertools::{Either, Itertools}; use rustc_data_structures::fx::FxHashSet; -use rustc_middle::mir::{Body, Local}; -use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_middle::mir::visit::{TyContext, Visitor}; +use rustc_middle::mir::{Body, Local, Location, SourceInfo}; +use rustc_middle::ty::visit::TypeVisitable; +use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::ResultsCursor; @@ -11,7 +13,7 @@ use crate::{ constraints::OutlivesConstraintSet, facts::{AllFacts, AllFactsExt}, location::LocationTable, - region_infer::values::RegionValueElements, + region_infer::values::{LivenessValues, RegionValueElements}, universal_regions::UniversalRegions, }; @@ -42,11 +44,11 @@ pub(super) fn generate<'mir, 'tcx>( let free_regions = regions_that_outlive_free_regions( typeck.infcx.num_region_vars(), - &typeck.borrowck_context.universal_regions, + typeck.borrowck_context.universal_regions, &typeck.borrowck_context.constraints.outlives_constraints, ); let (relevant_live_locals, boring_locals) = - compute_relevant_live_locals(typeck.tcx(), &free_regions, &body); + compute_relevant_live_locals(typeck.tcx(), &free_regions, body); let facts_enabled = use_polonius || AllFacts::enabled(typeck.tcx()); let polonius_drop_used = facts_enabled.then(|| { @@ -65,6 +67,14 @@ pub(super) fn generate<'mir, 'tcx>( boring_locals, polonius_drop_used, ); + + // Mark regions that should be live where they appear within rvalues or within a call: like + // args, regions, and types. + record_regular_live_regions( + typeck.tcx(), + &mut typeck.borrowck_context.constraints.liveness_constraints, + body, + ); } // The purpose of `compute_relevant_live_locals` is to define the subset of `Local` @@ -132,3 +142,71 @@ fn regions_that_outlive_free_regions<'tcx>( // Return the final set of things we visited. outlives_free_region } + +/// Some variables are "regular live" at `location` -- i.e., they may be used later. This means that +/// all regions appearing in their type must be live at `location`. +fn record_regular_live_regions<'tcx>( + tcx: TyCtxt<'tcx>, + liveness_constraints: &mut LivenessValues, + body: &Body<'tcx>, +) { + let mut visitor = LiveVariablesVisitor { tcx, liveness_constraints }; + for (bb, data) in body.basic_blocks.iter_enumerated() { + visitor.visit_basic_block_data(bb, data); + } +} + +/// Visitor looking for regions that should be live within rvalues or calls. +struct LiveVariablesVisitor<'cx, 'tcx> { + tcx: TyCtxt<'tcx>, + liveness_constraints: &'cx mut LivenessValues, +} + +impl<'cx, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'cx, 'tcx> { + /// We sometimes have `args` within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_args(&mut self, args: &GenericArgsRef<'tcx>, location: Location) { + self.record_regions_live_at(*args, location); + self.super_args(args); + } + + /// We sometimes have `region`s within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_region(&mut self, region: Region<'tcx>, location: Location) { + self.record_regions_live_at(region, location); + self.super_region(region); + } + + /// We sometimes have `ty`s within an rvalue, or within a + /// call. Make them live at the location where they appear. + fn visit_ty(&mut self, ty: Ty<'tcx>, ty_context: TyContext) { + match ty_context { + TyContext::ReturnTy(SourceInfo { span, .. }) + | TyContext::YieldTy(SourceInfo { span, .. }) + | TyContext::UserTy(span) + | TyContext::LocalDecl { source_info: SourceInfo { span, .. }, .. } => { + span_bug!(span, "should not be visiting outside of the CFG: {:?}", ty_context); + } + TyContext::Location(location) => { + self.record_regions_live_at(ty, location); + } + } + + self.super_ty(ty); + } +} + +impl<'cx, 'tcx> LiveVariablesVisitor<'cx, 'tcx> { + /// Some variable is "regular live" at `location` -- i.e., it may be used later. This means that + /// all regions appearing in the type of `value` must be live at `location`. + fn record_regions_live_at(&mut self, value: T, location: Location) + where + T: TypeVisitable>, + { + debug!("record_regions_live_at(value={:?}, location={:?})", value, location); + self.tcx.for_each_free_region(&value, |live_region| { + let live_region_vid = live_region.as_var(); + self.liveness_constraints.add_location(live_region_vid, location); + }); + } +} diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index c621df371..45f7b07fd 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -100,7 +100,7 @@ pub(super) fn populate_access_facts<'a, 'tcx>( location_table, move_data, }; - extractor.visit_body(&body); + extractor.visit_body(body); facts.var_dropped_at.extend( dropped_at.iter().map(|&(local, location)| (local, location_table.mid_index(location))), diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index e616449cc..c718d57be 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -1,12 +1,12 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::WithSuccessors; -use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix}; +use rustc_index::bit_set::HybridBitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::for_liveness; use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; use rustc_middle::traits::query::DropckOutlivesResult; -use rustc_middle::ty::{RegionVid, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; @@ -16,9 +16,8 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::ResultsCursor; -use crate::dataflow::BorrowIndex; use crate::{ - region_infer::values::{self, PointIndex, RegionValueElements}, + region_infer::values::{self, LiveLoans, PointIndex, RegionValueElements}, type_check::liveness::local_use_map::LocalUseMap, type_check::liveness::polonius, type_check::NormalizeLocation, @@ -49,22 +48,17 @@ pub(super) fn trace<'mir, 'tcx>( boring_locals: Vec, polonius_drop_used: Option>, ) { - debug!("trace()"); - let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body); // When using `-Zpolonius=next`, compute the set of loans that can reach a given region. - let num_loans = typeck.borrowck_context.borrow_set.len(); - let mut inflowing_loans = SparseBitMatrix::new(num_loans); if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { - let borrowck_context = &typeck.borrowck_context; + let borrowck_context = &mut typeck.borrowck_context; let borrow_set = &borrowck_context.borrow_set; - let constraint_set = &borrowck_context.constraints.outlives_constraints; - - let num_region_vars = typeck.infcx.num_region_vars(); - let graph = constraint_set.graph(num_region_vars); + let mut live_loans = LiveLoans::new(borrow_set.len()); + let outlives_constraints = &borrowck_context.constraints.outlives_constraints; + let graph = outlives_constraints.graph(typeck.infcx.num_region_vars()); let region_graph = - graph.region_graph(&constraint_set, borrowck_context.universal_regions.fr_static); + graph.region_graph(outlives_constraints, borrowck_context.universal_regions.fr_static); // Traverse each issuing region's constraints, and record the loan as flowing into the // outlived region. @@ -75,9 +69,13 @@ pub(super) fn trace<'mir, 'tcx>( continue; } - inflowing_loans.insert(succ, loan); + live_loans.inflowing_loans.insert(succ, loan); } } + + // Store the inflowing loans in the liveness constraints: they will be used to compute live + // loans when liveness data is recorded there. + borrowck_context.constraints.liveness_constraints.loans = Some(live_loans); }; let cx = LivenessContext { @@ -88,7 +86,6 @@ pub(super) fn trace<'mir, 'tcx>( local_use_map, move_data, drop_data: FxIndexMap::default(), - inflowing_loans, }; let mut results = LivenessResults::new(cx); @@ -126,9 +123,6 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { /// Index indicating where each variable is assigned, used, or /// dropped. local_use_map: &'me LocalUseMap, - - /// Set of loans that flow into a given region, when using `-Zpolonius=next`. - inflowing_loans: SparseBitMatrix, } struct DropData<'tcx> { @@ -489,7 +483,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { } let move_paths = &self.flow_inits.analysis().move_data().move_paths; - move_paths[mpi].find_descendant(&move_paths, |mpi| state.contains(mpi)).is_some() + move_paths[mpi].find_descendant(move_paths, |mpi| state.contains(mpi)).is_some() } /// Returns `true` if the local variable (or some part of it) is initialized in @@ -519,14 +513,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { live_at: &IntervalSet, ) { debug!("add_use_live_facts_for(value={:?})", value); - - Self::make_all_regions_live( - self.elements, - &mut self.typeck, - value, - live_at, - &self.inflowing_loans, - ); + Self::make_all_regions_live(self.elements, self.typeck, value, live_at); } /// Some variable with type `live_ty` is "drop live" at `location` @@ -550,7 +537,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { dropped_local, dropped_ty, drop_locations, - values::location_set_str(self.elements, live_at.iter()), + values::pretty_print_points(self.elements, live_at.iter()), ); let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ @@ -577,15 +564,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { - Self::make_all_regions_live( - self.elements, - &mut self.typeck, - kind, - live_at, - &self.inflowing_loans, - ); - - polonius::add_drop_of_var_derefs_origin(&mut self.typeck, dropped_local, &kind); + Self::make_all_regions_live(self.elements, self.typeck, kind, live_at); + polonius::add_drop_of_var_derefs_origin(self.typeck, dropped_local, &kind); } } @@ -594,20 +574,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { typeck: &mut TypeChecker<'_, 'tcx>, value: impl TypeVisitable>, live_at: &IntervalSet, - inflowing_loans: &SparseBitMatrix, ) { debug!("make_all_regions_live(value={:?})", value); debug!( "make_all_regions_live: live_at={}", - values::location_set_str(elements, live_at.iter()), + values::pretty_print_points(elements, live_at.iter()), ); - // When using `-Zpolonius=next`, we want to record the loans that flow into this value's - // regions as being live at the given `live_at` points: this will be used to compute the - // location where a loan goes out of scope. - let num_loans = typeck.borrowck_context.borrow_set.len(); - let value_loans = &mut HybridBitSet::new_empty(num_loans); - value.visit_with(&mut for_liveness::FreeRegionsVisitor { tcx: typeck.tcx(), param_env: typeck.param_env, @@ -618,22 +591,9 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { .borrowck_context .constraints .liveness_constraints - .add_elements(live_region_vid, live_at); - - // There can only be inflowing loans for this region when we are using - // `-Zpolonius=next`. - if let Some(inflowing) = inflowing_loans.row(live_region_vid) { - value_loans.union(inflowing); - } + .add_points(live_region_vid, live_at); }, }); - - // Record the loans reaching the value. - if !value_loans.is_empty() { - for point in live_at.iter() { - typeck.borrowck_context.live_loans.union_row(point, value_loans); - } - } } fn compute_drop_data( diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 608d01039..5247d5f69 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -14,18 +14,16 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; -use rustc_index::bit_set::SparseBitMatrix; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ - InferCtxt, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin, + BoundRegion, BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin, }; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::AssertKind; use rustc_middle::mir::*; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; @@ -51,8 +49,6 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::ResultsCursor; -use crate::dataflow::BorrowIndex; -use crate::region_infer::values::PointIndex; use crate::session_diagnostics::{MoveUnsized, SimdShuffleLastConst}; use crate::{ borrow_set::BorrowSet, @@ -68,7 +64,7 @@ use crate::{ region_infer::TypeTest, type_check::free_region_relations::{CreateResult, UniversalRegionRelations}, universal_regions::{DefiningTy, UniversalRegions}, - BorrowckInferCtxt, Upvar, + BorrowckInferCtxt, }; macro_rules! span_mirbug { @@ -138,7 +134,7 @@ pub(crate) fn type_check<'mir, 'tcx>( flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, elements: &Rc, - upvars: &[Upvar<'tcx>], + upvars: &[&ty::CapturedPlace<'tcx>], use_polonius: bool, ) -> MirTypeckResults<'tcx> { let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body); @@ -166,9 +162,6 @@ pub(crate) fn type_check<'mir, 'tcx>( debug!(?normalized_inputs_and_output); - // When using `-Zpolonius=next`, liveness will record the set of live loans per point. - let mut live_loans = SparseBitMatrix::new(borrow_set.len()); - let mut borrowck_context = BorrowCheckContext { universal_regions, location_table, @@ -176,7 +169,6 @@ pub(crate) fn type_check<'mir, 'tcx>( all_facts, constraints: &mut constraints, upvars, - live_loans: &mut live_loans, }; let mut checker = TypeChecker::new( @@ -191,11 +183,11 @@ pub(crate) fn type_check<'mir, 'tcx>( checker.check_user_type_annotations(); let mut verifier = TypeVerifier::new(&mut checker, promoted); - verifier.visit_body(&body); + verifier.visit_body(body); checker.typeck_mir(body); - checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); - checker.check_signature_annotation(&body); + checker.equate_inputs_and_outputs(body, universal_regions, &normalized_inputs_and_output); + checker.check_signature_annotation(body); liveness::generate( &mut checker, @@ -232,7 +224,7 @@ pub(crate) fn type_check<'mir, 'tcx>( let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind()); if hidden_type.has_non_region_infer() { - let reported = infcx.tcx.sess.delay_span_bug( + let reported = infcx.tcx.sess.span_delayed_bug( decl.hidden_type.span, format!("could not resolve {:#?}", hidden_type.ty.kind()), ); @@ -243,7 +235,7 @@ pub(crate) fn type_check<'mir, 'tcx>( }) .collect(); - MirTypeckResults { constraints, universal_region_relations, opaque_type_values, live_loans } + MirTypeckResults { constraints, universal_region_relations, opaque_type_values } } fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { @@ -274,9 +266,9 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { #[track_caller] fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: String) { // We sometimes see MIR failures (notably predicate failures) due to - // the fact that we check rvalue sized predicates here. So use `delay_span_bug` + // the fact that we check rvalue sized predicates here. So use `span_delayed_bug` // to avoid reporting bugs in those cases. - tcx.sess.diagnostic().delay_span_bug(span, msg); + tcx.sess.dcx().span_delayed_bug(span, msg); } enum FieldAccessError { @@ -318,7 +310,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { .borrowck_context .constraints .liveness_constraints - .add_element(live_region_vid, location); + .add_location(live_region_vid, location); }); // HACK(compiler-errors): Constants that are gathered into Body.required_consts @@ -389,7 +381,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { self.cx.ascribe_user_type( constant.const_.ty(), UserType::TypeOf(uv.def, UserArgs { args: uv.args, user_self_ty: None }), - locations.span(&self.cx.body), + locations.span(self.cx.body), ); } } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { @@ -553,7 +545,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { let all_facts = &mut None; let mut constraints = Default::default(); let mut liveness_constraints = - LivenessValues::new(Rc::new(RegionValueElements::new(&promoted_body))); + LivenessValues::new(Rc::new(RegionValueElements::new(promoted_body))); // Don't try to add borrow_region facts for the promoted MIR let mut swap_constraints = |this: &mut Self| { @@ -570,7 +562,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { swap_constraints(self); - self.visit_body(&promoted_body); + self.visit_body(promoted_body); self.cx.typeck_mir(promoted_body); @@ -592,16 +584,16 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } self.cx.borrowck_context.constraints.outlives_constraints.push(constraint) } - for region in liveness_constraints.rows() { + for region in liveness_constraints.regions() { // If the region is live at at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region // at the location provided as an argument to this method - if liveness_constraints.get_elements(region).next().is_some() { + if liveness_constraints.is_live_anywhere(region) { self.cx .borrowck_context .constraints .liveness_constraints - .add_element(region, location); + .add_location(region, location); } } } @@ -857,11 +849,7 @@ struct BorrowCheckContext<'a, 'tcx> { all_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, pub(crate) constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - upvars: &'a [Upvar<'tcx>], - - /// The set of loans that are live at a given point in the CFG, filled in by `liveness::trace`, - /// when using `-Zpolonius=next`. - pub(crate) live_loans: &'a mut SparseBitMatrix, + upvars: &'a [&'a ty::CapturedPlace<'tcx>], } /// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions @@ -870,9 +858,6 @@ pub(crate) struct MirTypeckResults<'tcx> { pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, pub(crate) universal_region_relations: Frozen>, pub(crate) opaque_type_values: FxIndexMap, OpaqueHiddenType<'tcx>>, - - /// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`. - pub(crate) live_loans: SparseBitMatrix, } /// A collection of region constraints that must be satisfied for the @@ -899,7 +884,7 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { /// not otherwise appear in the MIR -- in particular, the /// late-bound regions that it instantiates at call-sites -- and /// hence it must report on their liveness constraints. - pub(crate) liveness_constraints: LivenessValues, + pub(crate) liveness_constraints: LivenessValues, pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, @@ -1018,16 +1003,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { reported_errors: Default::default(), }; - // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering + // FIXME(-Znext-solver): A bit dubious that we're only registering // predefined opaques in the typeck root. if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) { - checker.register_predefined_opaques_in_new_solver(); + checker.register_predefined_opaques_for_next_solver(); } checker } - pub(super) fn register_predefined_opaques_in_new_solver(&mut self) { + pub(super) fn register_predefined_opaques_for_next_solver(&mut self) { // OK to use the identity arguments for each opaque type key, since // we remap opaques from HIR typeck back to their definition params. let opaques: Vec<_> = self @@ -1082,7 +1067,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); if result.is_err() { - self.infcx.tcx.sess.delay_span_bug( + self.infcx.tcx.sess.span_delayed_bug( self.body.span, "failed re-defining predefined opaques in mir typeck", ); @@ -1127,7 +1112,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations, locations.span(self.body), category, - &mut self.borrowck_context.constraints, + self.borrowck_context.constraints, ) .convert_all(data); } @@ -1202,7 +1187,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.infcx.tcx } - #[instrument(skip(self, body, location), level = "debug")] + #[instrument(skip(self, body), level = "debug")] fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) { let tcx = self.tcx(); debug!("stmt kind: {:?}", stmt.kind); @@ -1387,7 +1372,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return; } }; - let (sig, map) = tcx.replace_late_bound_regions(sig, |br| { + let (sig, map) = tcx.instantiate_bound_regions(sig, |br| { use crate::renumber::RegionCtxt; let region_ctxt_fn = || { @@ -1401,10 +1386,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { }; self.infcx.next_region_var( - LateBoundRegion( + BoundRegion( term.source_info.span, br.kind, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, ), region_ctxt_fn, ) @@ -1443,7 +1428,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.borrowck_context .constraints .liveness_constraints - .add_element(region_vid, term_location); + .add_location(region_vid, term_location); } self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source); @@ -1854,7 +1839,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { for op in ops { self.check_operand(op, location); } - self.check_aggregate_rvalue(&body, rvalue, ak, ops, location) + self.check_aggregate_rvalue(body, rvalue, ak, ops, location) } Rvalue::Repeat(operand, len) => { @@ -1934,7 +1919,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -1959,7 +1944,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -1988,7 +1973,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty, ty_fn_ptr_from, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -2013,7 +1998,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_trait_ref( trait_ref, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { + unsize_to: Some(tcx.fold_regions(ty, |r, _| { + if let ty::ReVar(_) = r.kind() { + tcx.lifetimes.re_erased + } else { + r + } + })), + }, ); } @@ -2033,7 +2026,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .iter() .map(|predicate| predicate.with_self_ty(tcx, self_ty)), location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ); let outlives_predicate = tcx.mk_predicate(Binder::dummy( @@ -2044,7 +2037,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_predicate( outlives_predicate, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ); } @@ -2065,7 +2058,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty_from, *ty_to, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -2131,7 +2124,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *ty_elem, *ty_to, location.to_locations(), - ConstraintCategory::Cast, + ConstraintCategory::Cast { unsize_to: None }, ) { span_mirbug!( self, @@ -2292,7 +2285,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } Rvalue::Ref(region, _borrow_kind, borrowed_place) => { - self.add_reborrow_constraint(&body, location, *region, borrowed_place); + self.add_reborrow_constraint(body, location, *region, borrowed_place); } Rvalue::BinaryOp( @@ -2504,7 +2497,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tcx = self.infcx.tcx; let field = path_utils::is_upvar_field_projection( tcx, - &self.borrowck_context.upvars, + self.borrowck_context.upvars, borrowed_place.as_ref(), body, ); @@ -2660,13 +2653,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { location.to_locations(), DUMMY_SP, // irrelevant; will be overridden. ConstraintCategory::Boring, // same as above. - &mut self.borrowck_context.constraints, + self.borrowck_context.constraints, ) - .apply_closure_requirements( - &closure_requirements, - def_id.to_def_id(), - args, - ); + .apply_closure_requirements(closure_requirements, def_id.to_def_id(), args); } // Now equate closure args to regions inherited from `typeck_root_def_id`. Fixes #98589. @@ -2674,8 +2663,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id); let parent_args = match tcx.def_kind(def_id) { + DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => { + args.as_coroutine().parent_args() + } DefKind::Closure => args.as_closure().parent_args(), - DefKind::Coroutine => args.as_coroutine().parent_args(), DefKind::InlineConst => args.as_inline_const().parent_args(), other => bug!("unexpected item {:?}", other), }; @@ -2706,7 +2697,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!(?body.span); for (local, local_decl) in body.local_decls.iter_enumerated() { - self.check_local(&body, local, local_decl); + self.check_local(body, local, local_decl); } for (block, block_data) in body.basic_blocks.iter_enumerated() { @@ -2719,8 +2710,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { location.statement_index += 1; } - self.check_terminator(&body, block_data.terminator(), location); - self.check_iscleanup(&body, block_data); + self.check_terminator(body, block_data.terminator(), location); + self.check_iscleanup(body, block_data); } } } diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index c1f82e19c..ee0bd1310 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -107,12 +107,12 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> fn next_existential_region_var( &mut self, from_forall: bool, - _name: Option, + name: Option, ) -> ty::Region<'tcx> { let origin = NllRegionVariableOrigin::Existential { from_forall }; let reg_var = - self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(_name)); + self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name)); reg_var } diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 7897a5a63..2b83c7871 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -462,7 +462,6 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { // "Liberate" the late-bound regions. These correspond to // "local" free regions. - let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty); let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars( @@ -665,7 +664,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind: ty::BrEnv, }; - let env_region = ty::Region::new_late_bound(tcx, ty::INNERMOST, br); + let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); let closure_ty = tcx.closure_env_ty(def_id, args, env_region).unwrap(); // The "inputs" of the closure in the @@ -738,18 +737,6 @@ trait InferCtxtExt<'tcx> { ) -> T where T: TypeFoldable>; - - fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope( - &self, - mir_def_id: LocalDefId, - indices: &mut UniversalRegionIndices<'tcx>, - ); - - fn replace_late_bound_regions_with_nll_infer_vars_in_item( - &self, - mir_def_id: LocalDefId, - indices: &mut UniversalRegionIndices<'tcx>, - ); } impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { @@ -781,10 +768,10 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { where T: TypeFoldable>, { - let (value, _map) = self.tcx.replace_late_bound_regions(value, |br| { + let (value, _map) = self.tcx.instantiate_bound_regions(value, |br| { debug!(?br); let liberated_region = - ty::Region::new_free(self.tcx, all_outlive_scope.to_def_id(), br.kind); + ty::Region::new_late_param(self.tcx, all_outlive_scope.to_def_id(), br.kind); let region_vid = { let name = match br.kind.get_name() { Some(name) => name, @@ -800,61 +787,13 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> { }); value } - - /// Finds late-bound regions that do not appear in the parameter listing and adds them to the - /// indices vector. Typically, we identify late-bound regions as we process the inputs and - /// outputs of the closure/function. However, sometimes there are late-bound regions which do - /// not appear in the fn parameters but which are nonetheless in scope. The simplest case of - /// this are unused functions, like fn foo<'a>() { } (see e.g., #51351). Despite not being used, - /// users can still reference these regions (e.g., let x: &'a u32 = &22;), so we need to create - /// entries for them and store them in the indices map. This code iterates over the complete - /// set of late-bound regions and checks for any that we have not yet seen, adding them to the - /// inputs vector. - #[instrument(skip(self, indices))] - fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope( - &self, - mir_def_id: LocalDefId, - indices: &mut UniversalRegionIndices<'tcx>, - ) { - for_each_late_bound_region_in_recursive_scope(self.tcx, mir_def_id, |r| { - debug!(?r); - if !indices.indices.contains_key(&r) { - let region_vid = { - let name = r.get_name_or_anon(); - self.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) - }; - - debug!(?region_vid); - indices.insert_late_bound_region(r, region_vid.as_var()); - } - }); - } - - #[instrument(skip(self, indices))] - fn replace_late_bound_regions_with_nll_infer_vars_in_item( - &self, - mir_def_id: LocalDefId, - indices: &mut UniversalRegionIndices<'tcx>, - ) { - for_each_late_bound_region_in_item(self.tcx, mir_def_id, |r| { - debug!(?r); - if !indices.indices.contains_key(&r) { - let region_vid = { - let name = r.get_name_or_anon(); - self.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) - }; - - indices.insert_late_bound_region(r, region_vid.as_var()); - } - }); - } } impl<'tcx> UniversalRegionIndices<'tcx> { /// Initially, the `UniversalRegionIndices` map contains only the /// early-bound regions in scope. Once that is all setup, we come /// in later and instantiate the late-bound regions, and then we - /// insert the `ReFree` version of those into the map as + /// insert the `ReLateParam` version of those into the map as /// well. These are used for error reporting. fn insert_late_bound_region(&mut self, r: ty::Region<'tcx>, vid: ty::RegionVid) { debug!("insert_late_bound_region({:?}, {:?})", r, vid); @@ -929,11 +868,12 @@ fn for_each_late_bound_region_in_item<'tcx>( return; } - for bound_var in tcx.late_bound_vars(tcx.hir().local_def_id_to_hir_id(mir_def_id)) { + for bound_var in tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)) { let ty::BoundVariableKind::Region(bound_region) = bound_var else { continue; }; - let liberated_region = ty::Region::new_free(tcx, mir_def_id.to_def_id(), bound_region); + let liberated_region = + ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), bound_region); f(liberated_region); } } diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs index c5991e0bc..6ac8e1ba7 100644 --- a/compiler/rustc_borrowck/src/used_muts.rs +++ b/compiler/rustc_borrowck/src/used_muts.rs @@ -35,7 +35,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { never_initialized_mut_locals: &mut never_initialized_mut_locals, mbcx: self, }; - visitor.visit_body(&visitor.mbcx.body); + visitor.visit_body(visitor.mbcx.body); } // Take the union of the existed `used_mut` set with those variables we've found were -- cgit v1.2.3