diff options
Diffstat (limited to 'compiler/rustc_borrowck')
32 files changed, 1391 insertions, 1295 deletions
diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index fbf628e86..87c113f3e 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false [dependencies] either = "1.5.0" diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs index 5e9cec5c3..f185e402f 100644 --- a/compiler/rustc_borrowck/src/constraint_generation.rs +++ b/compiler/rustc_borrowck/src/constraint_generation.rs @@ -14,8 +14,8 @@ use crate::{ places_conflict, region_infer::values::LivenessValues, }; -pub(super) fn generate_constraints<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, +pub(super) fn generate_constraints<'tcx>( + infcx: &InferCtxt<'tcx>, liveness_constraints: &mut LivenessValues<RegionVid>, all_facts: &mut Option<AllFacts>, location_table: &LocationTable, @@ -31,14 +31,14 @@ pub(super) fn generate_constraints<'cx, 'tcx>( body, }; - for (bb, data) in body.basic_blocks().iter_enumerated() { + 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, 'cx, 'tcx> { - infcx: &'cg InferCtxt<'cx, 'tcx>, +struct ConstraintGeneration<'cg, 'tcx> { + infcx: &'cg InferCtxt<'tcx>, all_facts: &'cg mut Option<AllFacts>, location_table: &'cg LocationTable, liveness_constraints: &'cg mut LivenessValues<RegionVid>, @@ -46,7 +46,7 @@ struct ConstraintGeneration<'cg, 'cx, 'tcx> { body: &'cg Body<'tcx>, } -impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> { +impl<'cg, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'tcx> { fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) { self.super_basic_block_data(bb, data); } @@ -156,7 +156,7 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> { } } -impl<'cx, 'cg, 'tcx> ConstraintGeneration<'cx, 'cg, 'tcx> { +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 diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index a504d0c91..df0412813 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -21,10 +21,7 @@ pub(crate) struct OutlivesConstraintSet<'tcx> { impl<'tcx> OutlivesConstraintSet<'tcx> { pub(crate) fn push(&mut self, constraint: OutlivesConstraint<'tcx>) { - debug!( - "OutlivesConstraintSet::push({:?}: {:?} @ {:?}", - constraint.sup, constraint.sub, constraint.locations - ); + debug!("OutlivesConstraintSet::push({:?})", constraint); if constraint.sup == constraint.sub { // 'a: 'a is pretty uninteresting return; @@ -73,7 +70,7 @@ impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> { } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq)] pub struct OutlivesConstraint<'tcx> { // NB. The ordering here is not significant for correctness, but // it is for convenience. Before we dump the constraints in the @@ -105,8 +102,8 @@ impl<'tcx> fmt::Debug for OutlivesConstraint<'tcx> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!( formatter, - "({:?}: {:?}) due to {:?} ({:?})", - self.sup, self.sub, self.locations, self.variance_info + "({:?}: {:?}) due to {:?} ({:?}) ({:?})", + self.sup, self.sub, self.locations, self.variance_info, self.category, ) } } diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index efc17a173..b162095f8 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -31,9 +31,8 @@ pub fn get_body_with_borrowck_facts<'tcx>( def: ty::WithOptConstParam<LocalDefId>, ) -> BodyWithBorrowckFacts<'tcx> { let (input_body, promoted) = tcx.mir_promoted(def); - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).enter(|infcx| { - let input_body: &Body<'_> = &input_body.borrow(); - let promoted: &IndexVec<_, _> = &promoted.borrow(); - *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap() - }) + let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).build(); + let input_body: &Body<'_> = &input_body.borrow(); + let promoted: &IndexVec<_, _> = &promoted.borrow(); + *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap() } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 97d5a8d15..9f7a4d499 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -143,7 +143,7 @@ struct OutOfScopePrecomputer<'a, 'tcx> { impl<'a, 'tcx> OutOfScopePrecomputer<'a, 'tcx> { fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self { OutOfScopePrecomputer { - visited: BitSet::new_empty(body.basic_blocks().len()), + visited: BitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], body, regioncx, @@ -391,7 +391,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) | mir::StatementKind::Coverage(..) - | mir::StatementKind::CopyNonOverlapping(..) + | mir::StatementKind::Intrinsic(..) | mir::StatementKind::Nop => {} } } diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 1ef2b0ae9..02071ed6b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -56,7 +56,7 @@ impl<'tcx> UniverseInfo<'tcx> { ) { match self.0 { UniverseInfoInner::RelateTys { expected, found } => { - let err = mbcx.infcx.report_mismatched_types( + let err = mbcx.infcx.err_ctxt().report_mismatched_types( &cause, expected, found, @@ -238,20 +238,11 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - mbcx.infcx.tcx.infer_ctxt().enter_with_canonical( - cause.span, - &self.canonical_query, - |ref infcx, key, _| { - let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); - type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause); - try_extract_error_from_fulfill_cx( - fulfill_cx, - infcx, - placeholder_region, - error_region, - ) - }, - ) + let (ref infcx, key, _) = + mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); + let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); + type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause); + try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region) } } @@ -288,37 +279,24 @@ where placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - mbcx.infcx.tcx.infer_ctxt().enter_with_canonical( - cause.span, - &self.canonical_query, - |ref infcx, key, _| { - let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); - - let mut selcx = SelectionContext::new(infcx); - - // FIXME(lqd): Unify and de-duplicate the following with the actual - // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the - // `ObligationCause`. The normalization results are currently different between - // `AtExt::normalize` used in the query and `normalize` called below: the former fails - // to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check - // after #85499 lands to see if its fixes have erased this difference. - let (param_env, value) = key.into_parts(); - let Normalized { value: _, obligations } = rustc_trait_selection::traits::normalize( - &mut selcx, - param_env, - cause, - value.value, - ); - fulfill_cx.register_predicate_obligations(infcx, obligations); - - try_extract_error_from_fulfill_cx( - fulfill_cx, - infcx, - placeholder_region, - error_region, - ) - }, - ) + let (ref infcx, key, _) = + mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); + let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); + + let mut selcx = SelectionContext::new(infcx); + + // FIXME(lqd): Unify and de-duplicate the following with the actual + // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the + // `ObligationCause`. The normalization results are currently different between + // `AtExt::normalize` used in the query and `normalize` called below: the former fails + // to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check + // after #85499 lands to see if its fixes have erased this difference. + let (param_env, value) = key.into_parts(); + let Normalized { value: _, obligations } = + rustc_trait_selection::traits::normalize(&mut selcx, param_env, cause, value.value); + fulfill_cx.register_predicate_obligations(infcx, obligations); + + try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region) } } @@ -349,21 +327,11 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> { placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - mbcx.infcx.tcx.infer_ctxt().enter_with_canonical( - cause.span, - &self.canonical_query, - |ref infcx, key, _| { - let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); - type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span)) - .ok()?; - try_extract_error_from_fulfill_cx( - fulfill_cx, - infcx, - placeholder_region, - error_region, - ) - }, - ) + let (ref infcx, key, _) = + mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); + let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); + type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span)).ok()?; + try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region) } } @@ -407,7 +375,7 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { #[instrument(skip(fulfill_cx, infcx), level = "debug")] fn try_extract_error_from_fulfill_cx<'tcx>( mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { @@ -427,7 +395,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>( } fn try_extract_error_from_region_constraints<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, region_constraints: &RegionConstraintData<'tcx>, @@ -449,43 +417,37 @@ fn try_extract_error_from_region_constraints<'tcx>( })?; debug!(?sub_region, "cause = {:#?}", cause); - let nice_error = match (error_region, *sub_region) { - (Some(error_region), ty::ReVar(vid)) => NiceRegionError::new( - infcx, - RegionResolutionError::SubSupConflict( - vid, - region_var_origin(vid), - cause.clone(), - error_region, - cause.clone(), - placeholder_region, - vec![], - ), - ), - (Some(error_region), _) => NiceRegionError::new( - infcx, - RegionResolutionError::ConcreteFailure(cause.clone(), error_region, placeholder_region), + let error = match (error_region, *sub_region) { + (Some(error_region), ty::ReVar(vid)) => RegionResolutionError::SubSupConflict( + vid, + region_var_origin(vid), + cause.clone(), + error_region, + cause.clone(), + placeholder_region, + vec![], ), + (Some(error_region), _) => { + RegionResolutionError::ConcreteFailure(cause.clone(), error_region, placeholder_region) + } // Note universe here is wrong... - (None, ty::ReVar(vid)) => NiceRegionError::new( - infcx, - RegionResolutionError::UpperBoundUniverseConflict( - vid, - region_var_origin(vid), - universe_of_region(vid), - cause.clone(), - placeholder_region, - ), - ), - (None, _) => NiceRegionError::new( - infcx, - RegionResolutionError::ConcreteFailure(cause.clone(), sub_region, placeholder_region), + (None, ty::ReVar(vid)) => RegionResolutionError::UpperBoundUniverseConflict( + vid, + region_var_origin(vid), + universe_of_region(vid), + cause.clone(), + placeholder_region, ), + (None, _) => { + RegionResolutionError::ConcreteFailure(cause.clone(), sub_region, placeholder_region) + } }; - nice_error.try_report_from_nll().or_else(|| { + NiceRegionError::new(&infcx.err_ctxt(), error).try_report_from_nll().or_else(|| { if let SubregionOrigin::Subtype(trace) = cause { Some( - infcx.report_and_explain_type_error(*trace, &TypeError::RegionsPlaceholderMismatch), + infcx + .err_ctxt() + .report_and_explain_type_error(*trace, TypeError::RegionsPlaceholderMismatch), ) } else { None diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 8bc8964bb..583bc2e28 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -16,7 +16,7 @@ use rustc_middle::mir::{ FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty}; +use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; @@ -198,7 +198,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_span, move_spans, *moved_place, - Some(used_place), partially_str, loop_message, move_msg, @@ -258,7 +257,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = place.ty(self.body, self.infcx.tcx).ty; // If we're in pattern, we do nothing in favor of the previous suggestion (#80913). - if is_loop_move & !in_pattern { + // Same for if we're in a loop, see #101119. + if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) { if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { // We have a `&mut` ref, we need to reborrow on each iteration (#62112). err.span_suggestion_verbose( @@ -368,6 +368,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; visitor.visit_body(&body); + let mut show_assign_sugg = false; let isnt_initialized = if let InitializationRequiringAction::PartialAssignment | InitializationRequiringAction::Assignment = desired_action { @@ -395,6 +396,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .count() == 0 { + show_assign_sugg = true; "isn't initialized" } else { "is possibly-uninitialized" @@ -445,13 +447,87 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } } + err.span_label(decl_span, "binding declared here but left uninitialized"); + if show_assign_sugg { + struct LetVisitor { + decl_span: Span, + sugg_span: Option<Span>, + } + + impl<'v> Visitor<'v> for LetVisitor { + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { + if self.sugg_span.is_some() { + return; + } + if let hir::StmtKind::Local(hir::Local { + span, ty, init: None, .. + }) = &ex.kind && span.contains(self.decl_span) { + self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span)); + } + hir::intravisit::walk_stmt(self, ex); + } + } + + let mut visitor = LetVisitor { decl_span, sugg_span: None }; + visitor.visit_body(&body); + if let Some(span) = visitor.sugg_span { + self.suggest_assign_value(&mut err, moved_place, span); + } + } err } + fn suggest_assign_value( + &self, + err: &mut Diagnostic, + moved_place: PlaceRef<'tcx>, + sugg_span: Span, + ) { + let ty = moved_place.ty(self.body, self.infcx.tcx).ty; + debug!("ty: {:?}, kind: {:?}", ty, ty.kind()); + + let tcx = self.infcx.tcx; + let implements_default = |ty, param_env| { + let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { + return false; + }; + // Regions are already solved, so we must use a fresh InferCtxt, + // but the type has region variables, so erase those. + tcx.infer_ctxt() + .build() + .type_implements_trait( + default_trait, + tcx.erase_regions(ty), + ty::List::empty(), + param_env, + ) + .must_apply_modulo_regions() + }; + + let assign_value = match ty.kind() { + ty::Bool => "false", + ty::Float(_) => "0.0", + ty::Int(_) | ty::Uint(_) => "0", + ty::Never | ty::Error(_) => "", + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]", + ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()", + _ => "todo!()", + }; + + if !assign_value.is_empty() { + err.span_suggestion_verbose( + sugg_span.shrink_to_hi(), + format!("consider assigning a value"), + format!(" = {}", assign_value), + Applicability::MaybeIncorrect, + ); + } + } + fn suggest_borrow_fn_like( &self, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, ty: Ty<'tcx>, move_sites: &[MoveSite], value_name: &str, @@ -526,12 +602,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { true } - fn suggest_adding_copy_bounds( - &self, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, - ty: Ty<'tcx>, - span: Span, - ) { + fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) { let tcx = self.infcx.tcx; let generics = tcx.generics_of(self.mir_def_id()); @@ -541,41 +612,40 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .and_then(|def_id| tcx.hir().get_generics(def_id)) else { return; }; // Try to find predicates on *generic params* that would allow copying `ty` - let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| { - let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx); + let infcx = tcx.infer_ctxt().build(); + let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx); - let copy_did = infcx.tcx.lang_items().copy_trait().unwrap(); - let cause = ObligationCause::new( - span, - self.mir_hir_id(), - rustc_infer::traits::ObligationCauseCode::MiscObligation, - ); - fulfill_cx.register_bound( - &infcx, - self.param_env, - // Erase any region vids from the type, which may not be resolved - infcx.tcx.erase_regions(ty), - copy_did, - cause, - ); - // Select all, including ambiguous predicates - let errors = fulfill_cx.select_all_or_error(&infcx); - - // Only emit suggestion if all required predicates are on generic - errors - .into_iter() - .map(|err| match err.obligation.predicate.kind().skip_binder() { - PredicateKind::Trait(predicate) => match predicate.self_ty().kind() { - ty::Param(param_ty) => Ok(( - generics.type_param(param_ty, tcx), - predicate.trait_ref.print_only_trait_path().to_string(), - )), - _ => Err(()), - }, + let copy_did = infcx.tcx.lang_items().copy_trait().unwrap(); + let cause = ObligationCause::new( + span, + self.mir_hir_id(), + rustc_infer::traits::ObligationCauseCode::MiscObligation, + ); + fulfill_cx.register_bound( + &infcx, + self.param_env, + // Erase any region vids from the type, which may not be resolved + infcx.tcx.erase_regions(ty), + copy_did, + cause, + ); + // Select all, including ambiguous predicates + let errors = fulfill_cx.select_all_or_error(&infcx); + + // Only emit suggestion if all required predicates are on generic + let predicates: Result<Vec<_>, _> = errors + .into_iter() + .map(|err| match err.obligation.predicate.kind().skip_binder() { + PredicateKind::Trait(predicate) => match predicate.self_ty().kind() { + ty::Param(param_ty) => Ok(( + generics.type_param(param_ty, tcx), + predicate.trait_ref.print_only_trait_path().to_string(), + )), _ => Err(()), - }) - .collect() - }); + }, + _ => Err(()), + }) + .collect(); if let Ok(predicates) = predicates { suggest_constraining_type_params( @@ -1124,6 +1194,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// short a lifetime. (But sometimes it is more useful to report /// it as a more direct conflict between the execution of a /// `Drop::drop` with an aliasing borrow.) + #[instrument(level = "debug", skip(self))] pub(crate) fn report_borrowed_value_does_not_live_long_enough( &mut self, location: Location, @@ -1131,13 +1202,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place_span: (Place<'tcx>, Span), kind: Option<WriteKind>, ) { - debug!( - "report_borrowed_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}\ - )", - location, borrow, place_span, kind - ); - let drop_span = place_span.1; let root_place = self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap(); @@ -1194,10 +1258,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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); - debug!( - "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})", - place_desc, explanation - ); + debug!(?place_desc, ?explanation); + let err = match (place_desc, explanation) { // If the outlives constraint comes from inside the closure, // for example: @@ -1469,6 +1531,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err } + #[instrument(level = "debug", skip(self))] fn report_temporary_value_does_not_live_long_enough( &mut self, location: Location, @@ -1478,13 +1541,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { proper_span: Span, explanation: BorrowExplanation<'tcx>, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { - debug!( - "report_temporary_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}\ - )", - location, borrow, drop_span, proper_span - ); - if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } = explanation { @@ -2164,7 +2220,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } StorageDeadOrDrop::Destructor(_) => kind, }, - ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { + ProjectionElem::OpaqueCast { .. } + | ProjectionElem::Field(..) + | ProjectionElem::Downcast(..) => { match place_ty.ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { // Report the outermost adt with a destructor diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 72aee0267..582d683dd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -1,8 +1,5 @@ //! Print diagnostics to explain why values are borrowed. -use std::collections::VecDeque; - -use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic}; use rustc_index::vec::IndexVec; use rustc_infer::infer::NllRegionVariableOrigin; @@ -15,7 +12,7 @@ use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, DesugaringKind, Span}; -use crate::region_infer::BlameConstraint; +use crate::region_infer::{BlameConstraint, ExtraConstraintInfo}; use crate::{ borrow_set::BorrowData, nll::ConstraintDescription, region_infer::Cause, MirBorrowckCtxt, WriteKind, @@ -38,6 +35,7 @@ pub(crate) enum BorrowExplanation<'tcx> { span: Span, region_name: RegionName, opt_place_desc: Option<String>, + extra_info: Vec<ExtraConstraintInfo>, }, Unexplained, } @@ -243,6 +241,7 @@ impl<'tcx> BorrowExplanation<'tcx> { ref region_name, ref opt_place_desc, from_closure: _, + ref extra_info, } => { region_name.highlight_region_name(err); @@ -268,18 +267,30 @@ impl<'tcx> BorrowExplanation<'tcx> { ); }; + for extra in extra_info { + match extra { + ExtraConstraintInfo::PlaceholderFromPredicate(span) => { + err.span_note(*span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime")); + } + } + } + self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); } _ => {} } } - pub(crate) fn add_lifetime_bound_suggestion_to_diagnostic( + + fn add_lifetime_bound_suggestion_to_diagnostic( &self, err: &mut Diagnostic, category: &ConstraintCategory<'tcx>, span: Span, region_name: &RegionName, ) { + if !span.is_desugaring(DesugaringKind::OpaqueTy) { + return; + } if let ConstraintCategory::OpaqueType = category { let suggestable_name = if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime }; @@ -305,18 +316,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, borrow_region: RegionVid, outlived_region: RegionVid, - ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>) { - let BlameConstraint { category, from_closure, cause, variance_info: _ } = - self.regioncx.best_blame_constraint( - &self.body, - borrow_region, - NllRegionVariableOrigin::FreeRegion, - |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region), - ); + ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>) { + let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint( + borrow_region, + NllRegionVariableOrigin::FreeRegion, + |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region), + ); + let BlameConstraint { category, from_closure, cause, .. } = blame_constraint; let outlived_fr_name = self.give_region_a_name(outlived_region); - (category, from_closure, cause.span, outlived_fr_name) + (category, from_closure, cause.span, outlived_fr_name, extra_info) } /// Returns structured explanation for *why* the borrow contains the @@ -332,37 +342,51 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// - second half is the place being accessed /// /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points + #[instrument(level = "debug", skip(self))] pub(crate) fn explain_why_borrow_contains_point( &self, location: Location, borrow: &BorrowData<'tcx>, kind_place: Option<(WriteKind, Place<'tcx>)>, ) -> BorrowExplanation<'tcx> { - debug!( - "explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})", - location, borrow, kind_place - ); - let regioncx = &self.regioncx; let body: &Body<'_> = &self.body; let tcx = self.infcx.tcx; let borrow_region_vid = borrow.region; - debug!("explain_why_borrow_contains_point: borrow_region_vid={:?}", borrow_region_vid); - - let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location); - debug!("explain_why_borrow_contains_point: region_sub={:?}", region_sub); + debug!(?borrow_region_vid); + + let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location); + debug!(?region_sub); + + let mut use_location = location; + let mut use_in_later_iteration_of_loop = false; + + if region_sub == borrow_region_vid { + // When `region_sub` is the same as `borrow_region_vid` (the location where the borrow is + // issued is the same location that invalidates the reference), this is likely a loop iteration + // - in this case, try using the loop terminator location in `find_sub_region_live_at`. + if let Some(loop_terminator_location) = + regioncx.find_loop_terminator_location(borrow.region, body) + { + region_sub = self + .regioncx + .find_sub_region_live_at(borrow_region_vid, loop_terminator_location); + debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub); + use_location = loop_terminator_location; + use_in_later_iteration_of_loop = true; + } + } - match find_use::find(body, regioncx, tcx, region_sub, location) { + match find_use::find(body, regioncx, tcx, region_sub, use_location) { Some(Cause::LiveVar(local, location)) => { let span = body.source_info(location).span; let spans = self .move_spans(Place::from(local).as_ref(), location) .or_else(|| self.borrow_spans(span, location)); - let borrow_location = location; - if self.is_use_in_later_iteration_of_loop(borrow_location, location) { - let later_use = self.later_use_kind(borrow, spans, location); + if use_in_later_iteration_of_loop { + let later_use = self.later_use_kind(borrow, spans, use_location); BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2) } else { // Check if the location represents a `FakeRead`, and adapt the error @@ -392,7 +416,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None => { if let Some(region) = self.to_error_region_vid(borrow_region_vid) { - let (category, from_closure, span, region_name) = + let (category, from_closure, span, region_name, extra_info) = self.free_region_constraint_info(borrow_region_vid, region); if let Some(region_name) = region_name { let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref()); @@ -402,150 +426,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { span, region_name, opt_place_desc, + extra_info, } } else { - debug!( - "explain_why_borrow_contains_point: \ - Could not generate a region name" - ); + debug!("Could not generate a region name"); BorrowExplanation::Unexplained } } else { - debug!( - "explain_why_borrow_contains_point: \ - Could not generate an error region vid" - ); + debug!("Could not generate an error region vid"); BorrowExplanation::Unexplained } } } } - /// true if `borrow_location` can reach `use_location` by going through a loop and - /// `use_location` is also inside of that loop - fn is_use_in_later_iteration_of_loop( - &self, - borrow_location: Location, - use_location: Location, - ) -> bool { - let back_edge = self.reach_through_backedge(borrow_location, use_location); - back_edge.map_or(false, |back_edge| self.can_reach_head_of_loop(use_location, back_edge)) - } - - /// Returns the outmost back edge if `from` location can reach `to` location passing through - /// that back edge - fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> { - let mut visited_locations = FxHashSet::default(); - let mut pending_locations = VecDeque::new(); - visited_locations.insert(from); - pending_locations.push_back(from); - debug!("reach_through_backedge: from={:?} to={:?}", from, to,); - - let mut outmost_back_edge = None; - while let Some(location) = pending_locations.pop_front() { - debug!( - "reach_through_backedge: location={:?} outmost_back_edge={:?} - pending_locations={:?} visited_locations={:?}", - location, outmost_back_edge, pending_locations, visited_locations - ); - - if location == to && outmost_back_edge.is_some() { - // We've managed to reach the use location - debug!("reach_through_backedge: found!"); - return outmost_back_edge; - } - - let block = &self.body.basic_blocks()[location.block]; - - if location.statement_index < block.statements.len() { - let successor = location.successor_within_block(); - if visited_locations.insert(successor) { - pending_locations.push_back(successor); - } - } else { - pending_locations.extend( - block - .terminator() - .successors() - .map(|bb| Location { statement_index: 0, block: bb }) - .filter(|s| visited_locations.insert(*s)) - .map(|s| { - if self.is_back_edge(location, s) { - match outmost_back_edge { - None => { - outmost_back_edge = Some(location); - } - - Some(back_edge) - if location.dominates(back_edge, &self.dominators) => - { - outmost_back_edge = Some(location); - } - - Some(_) => {} - } - } - - s - }), - ); - } - } - - None - } - - /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the - /// intermediate nodes - fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool { - self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default()) - } - - fn find_loop_head_dfs( - &self, - from: Location, - loop_head: Location, - visited_locations: &mut FxHashSet<Location>, - ) -> bool { - visited_locations.insert(from); - - if from == loop_head { - return true; - } - - if loop_head.dominates(from, &self.dominators) { - let block = &self.body.basic_blocks()[from.block]; - - if from.statement_index < block.statements.len() { - let successor = from.successor_within_block(); - - if !visited_locations.contains(&successor) - && self.find_loop_head_dfs(successor, loop_head, visited_locations) - { - return true; - } - } else { - for bb in block.terminator().successors() { - let successor = Location { statement_index: 0, block: bb }; - - if !visited_locations.contains(&successor) - && self.find_loop_head_dfs(successor, loop_head, visited_locations) - { - return true; - } - } - } - } - - false - } - - /// True if an edge `source -> target` is a backedge -- in other words, if the target - /// dominates the source. - fn is_back_edge(&self, source: Location, target: Location) -> bool { - target.dominates(source, &self.dominators) - } - /// Determine how the borrow was later used. /// First span returned points to the location of the conflicting use /// Second span if `Some` is returned in the case of closures and points @@ -564,7 +458,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { UseSpans::PatUse(span) | UseSpans::OtherUse(span) | UseSpans::FnSelfUse { var_span: span, .. } => { - let block = &self.body.basic_blocks()[location.block]; + let block = &self.body.basic_blocks[location.block]; let kind = if let Some(&Statement { kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)), diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 098e8de94..534d9ecae 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -237,6 +237,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } ProjectionElem::Downcast(..) if opt.including_downcast => return None, ProjectionElem::Downcast(..) => (), + ProjectionElem::OpaqueCast(..) => (), ProjectionElem::Field(field, _ty) => { // FIXME(project-rfc_2229#36): print capture precisely here. if let Some(field) = self.is_upvar_field_projection(PlaceRef { @@ -317,6 +318,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx) } ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx), + ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty), ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), }, }; @@ -970,7 +972,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_span: Span, move_spans: UseSpans<'tcx>, moved_place: Place<'tcx>, - used_place: Option<PlaceRef<'tcx>>, partially_str: &str, loop_message: &str, move_msg: &str, @@ -1024,7 +1025,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { let ty = moved_place.ty(self.body, self.infcx.tcx).ty; let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) { - Some(def_id) => self.infcx.tcx.infer_ctxt().enter(|infcx| { + Some(def_id) => { + let infcx = self.infcx.tcx.infer_ctxt().build(); type_known_to_meet_bound_modulo_regions( &infcx, self.param_env, @@ -1035,7 +1037,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { def_id, DUMMY_SP, ) - }), + } _ => false, }; if suggest { @@ -1058,9 +1060,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place_name, partially_str, loop_message ), ); - // If we have a `&mut` ref, we need to reborrow. - if let Some(ty::Ref(_, _, hir::Mutability::Mut)) = used_place - .map(|used_place| used_place.ty(self.body, self.infcx.tcx).ty.kind()) + // If the moved place was a `&mut` ref, then we can + // suggest to reborrow it where it was moved, so it + // will still be valid by the time we get to the usage. + if let ty::Ref(_, _, hir::Mutability::Mut) = + moved_place.ty(self.body, self.infcx.tcx).ty.kind() { // If we are in a loop this will be suggested later. if !is_loop_move { @@ -1086,14 +1090,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ), ); } - if is_option_or_result && maybe_reinitialized_locations_is_empty { - err.span_suggestion_verbose( - fn_call_span.shrink_to_lo(), - "consider calling `.as_ref()` to borrow the type's contents", - "as_ref().", - Applicability::MachineApplicable, - ); - } // Avoid pointing to the same function in multiple different // error messages. if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { @@ -1102,6 +1098,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!("this function takes ownership of the receiver `self`, which moves {}", place_name) ); } + if is_option_or_result && maybe_reinitialized_locations_is_empty { + err.span_label( + var_span, + "help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents", + ); + } } // 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 cb3cd479a..5a47f4567 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -88,7 +88,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(StatementKind::Assign(box ( place, Rvalue::Use(Operand::Move(move_from)), - ))) = self.body.basic_blocks()[location.block] + ))) = self.body.basic_blocks[location.block] .statements .get(location.statement_index) .map(|stmt| &stmt.kind) @@ -360,7 +360,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { diag.span_label(upvar_span, "captured outer variable"); diag.span_label( - self.body.span, + self.infcx.tcx.def_span(def_id), format!("captured by this `{closure_kind}` closure"), ); @@ -401,7 +401,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(use_spans) = use_spans { self.explain_captures( - &mut err, span, span, use_spans, move_place, None, "", "", "", false, true, + &mut err, span, span, use_spans, move_place, "", "", "", false, true, ); } err diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 0ad4abbce..8ad40c0aa 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1,23 +1,23 @@ +use rustc_errors::{ + Applicability, Diagnostic, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, +}; use rustc_hir as hir; +use rustc_hir::intravisit::Visitor; use rustc_hir::Node; use rustc_middle::hir::map::Map; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{ hir::place::PlaceBase, - mir::{ - self, BindingForm, ClearCrossCrate, ImplicitSelfKind, Local, LocalDecl, LocalInfo, - LocalKind, Location, - }, + mir::{self, BindingForm, ClearCrossCrate, Local, LocalDecl, LocalInfo, LocalKind, Location}, }; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{BytePos, Span}; +use rustc_span::{sym, BytePos, Span}; use crate::diagnostics::BorrowedContentSource; use crate::MirBorrowckCtxt; use rustc_const_eval::util::collect_writes::FindAssignments; -use rustc_errors::{Applicability, Diagnostic}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) enum AccessKind { @@ -169,6 +169,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .., ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), ], @@ -309,7 +310,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && !matches!( decl.local_info, Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( - ImplicitSelfKind::MutRef + hir::ImplicitSelfKind::MutRef )))) ) { @@ -364,7 +365,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) && let hir::PatKind::Binding( - hir::BindingAnnotation::Unannotated, + hir::BindingAnnotation::NONE, _, upvar_ident, _, @@ -614,6 +615,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { "trait `IndexMut` is required to modify indexed content, \ but it is not implemented for `{ty}`", )); + self.suggest_map_index_mut_alternatives(ty, &mut err, span); } _ => (), } @@ -627,6 +629,127 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.buffer_error(err); } + fn suggest_map_index_mut_alternatives( + &self, + ty: Ty<'_>, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + span: Span, + ) { + let Some(adt) = ty.ty_adt_def() else { return }; + let did = adt.did(); + if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) + || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did) + { + struct V<'a, 'b, 'tcx, G: EmissionGuarantee> { + assign_span: Span, + err: &'a mut DiagnosticBuilder<'b, G>, + ty: Ty<'tcx>, + suggested: bool, + } + impl<'a, 'b: 'a, 'hir, 'tcx, G: EmissionGuarantee> Visitor<'hir> for V<'a, 'b, 'tcx, G> { + fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'hir>) { + hir::intravisit::walk_stmt(self, stmt); + let expr = match stmt.kind { + hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, + hir::StmtKind::Local(hir::Local { init: Some(expr), .. }) => expr, + _ => { + return; + } + }; + if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind + && let hir::ExprKind::Index(val, index) = place.kind + && (expr.span == self.assign_span || place.span == self.assign_span) + { + // val[index] = rv; + // ---------- place + self.err.multipart_suggestions( + &format!( + "to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API", + self.ty, + ), + vec![ + vec![ // val.insert(index, rv); + ( + val.span.shrink_to_hi().with_hi(index.span.lo()), + ".insert(".to_string(), + ), + ( + index.span.shrink_to_hi().with_hi(rv.span.lo()), + ", ".to_string(), + ), + (rv.span.shrink_to_hi(), ")".to_string()), + ], + vec![ // val.get_mut(index).map(|v| { *v = rv; }); + ( + val.span.shrink_to_hi().with_hi(index.span.lo()), + ".get_mut(".to_string(), + ), + ( + index.span.shrink_to_hi().with_hi(place.span.hi()), + ").map(|val| { *val".to_string(), + ), + ( + rv.span.shrink_to_hi(), + "; })".to_string(), + ), + ], + vec![ // let x = val.entry(index).or_insert(rv); + (val.span.shrink_to_lo(), "let val = ".to_string()), + ( + val.span.shrink_to_hi().with_hi(index.span.lo()), + ".entry(".to_string(), + ), + ( + index.span.shrink_to_hi().with_hi(rv.span.lo()), + ").or_insert(".to_string(), + ), + (rv.span.shrink_to_hi(), ")".to_string()), + ], + ].into_iter(), + Applicability::MachineApplicable, + ); + self.suggested = true; + } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind + && let hir::ExprKind::Index(val, index) = receiver.kind + && expr.span == self.assign_span + { + // val[index].path(args..); + self.err.multipart_suggestion( + &format!("to modify a `{}` use `.get_mut()`", self.ty), + vec![ + ( + val.span.shrink_to_hi().with_hi(index.span.lo()), + ".get_mut(".to_string(), + ), + ( + index.span.shrink_to_hi().with_hi(receiver.span.hi()), + ").map(|val| val".to_string(), + ), + (sp.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + self.suggested = true; + } + } + } + let hir_map = self.infcx.tcx.hir(); + let def_id = self.body.source.def_id(); + let hir_id = hir_map.local_def_id_to_hir_id(def_id.as_local().unwrap()); + let node = hir_map.find(hir_id); + let Some(hir::Node::Item(item)) = node else { return; }; + let hir::ItemKind::Fn(.., body_id) = item.kind else { return; }; + let body = self.infcx.tcx.hir().body(body_id); + let mut v = V { assign_span: span, err, ty, suggested: false }; + v.visit_body(body); + if !v.suggested { + err.help(&format!( + "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API", + )); + } + } + } + /// User cannot make signature of a trait mutable without changing the /// trait. So we find if this error belongs to a trait and if so we move /// suggestion to the trait or disable it if it is out of scope of this crate @@ -786,11 +909,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { [ Expr { kind: - MethodCall( - path_segment, - _args, - span, - ), + MethodCall(path_segment, _, _, span), hir_id, .. }, @@ -810,10 +929,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { _, ) = hir_map.body(fn_body_id).value.kind { - let opt_suggestions = path_segment - .hir_id - .map(|path_hir_id| self.infcx.tcx.typeck(path_hir_id.owner)) - .and_then(|typeck| typeck.type_dependent_def_id(*hir_id)) + let opt_suggestions = self + .infcx + .tcx + .typeck(path_segment.hir_id.owner.def_id) + .type_dependent_def_id(*hir_id) .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id)) .map(|def_id| self.infcx.tcx.associated_items(def_id)) .map(|assoc_items| { @@ -851,6 +971,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let hir = self.infcx.tcx.hir(); let closure_id = self.mir_hir_id(); + let closure_span = self.infcx.tcx.def_span(self.mir_def_id()); let fn_call_id = hir.get_parent_node(closure_id); let node = hir.get(fn_call_id); let def_id = hir.enclosing_body_owner(fn_call_id); @@ -902,7 +1023,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(span) = arg { err.span_label(span, "change this to accept `FnMut` instead of `Fn`"); err.span_label(func.span, "expects `Fn` instead of `FnMut`"); - err.span_label(self.body.span, "in this closure"); + err.span_label(closure_span, "in this closure"); look_at_return = false; } } @@ -911,7 +1032,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)) { + match hir.get_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, @@ -928,7 +1049,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { sig.decl.output.span(), "change this to return `FnMut` instead of `Fn`", ); - err.span_label(self.body.span, "in this closure"); + err.span_label(closure_span, "in this closure"); } _ => {} } @@ -952,7 +1073,7 @@ fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symb // // Deliberately fall into this case for all implicit self types, // so that we don't fall in to the next case with them. - *kind == mir::ImplicitSelfKind::MutRef + *kind == hir::ImplicitSelfKind::MutRef } _ if Some(kw::SelfLower) == local_name => { // Otherwise, check if the name is the `self` keyword - in which case diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index d359d7efb..35c3df768 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -6,7 +6,6 @@ use rustc_errors::Diagnostic; use rustc_middle::ty::RegionVid; use smallvec::SmallVec; use std::collections::BTreeMap; -use tracing::debug; use crate::MirBorrowckCtxt; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 176090c3b..15230718d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,3 +1,5 @@ +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] //! Error reporting machinery for lifetime errors. use rustc_data_structures::fx::FxHashSet; @@ -23,10 +25,13 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use crate::borrowck_errors; -use crate::session_diagnostics::GenericDoesNotLiveLongEnough; +use crate::session_diagnostics::{ + FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, + LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, +}; use super::{OutlivesSuggestionBuilder, RegionName}; -use crate::region_infer::BlameConstraint; +use crate::region_infer::{BlameConstraint, ExtraConstraintInfo}; use crate::{ nll::ConstraintDescription, region_infer::{values::RegionElement, TypeTest}, @@ -181,7 +186,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(lower_bound_region) = lower_bound_region { let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx); let origin = RelateParamBound(type_test_span, generic_ty, None); - self.buffer_error(self.infcx.construct_generic_bound_failure( + self.buffer_error(self.infcx.err_ctxt().construct_generic_bound_failure( self.body.source.def_id().expect_local(), type_test_span, Some(origin), @@ -229,7 +234,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. let (_, cause) = self.regioncx.find_outlives_blame_span( - &self.body, longer_fr, NllRegionVariableOrigin::Placeholder(placeholder), error_vid, @@ -277,7 +281,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let tcx = self.infcx.tcx; match tcx.hir().get_if_local(def_id) { Some(Node::ImplItem(impl_item)) => { - match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id())) { + match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id()).def_id) + { Some(Node::Item(Item { kind: ItemKind::Impl(hir::Impl { self_ty, .. }), .. @@ -287,7 +292,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } Some(Node::TraitItem(trait_item)) => { let trait_did = tcx.hir().get_parent_item(trait_item.hir_id()); - match tcx.hir().find_by_def_id(trait_did) { + match tcx.hir().find_by_def_id(trait_did.def_id) { Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => { // The method being called is defined in the `trait`, but the `'static` // obligation comes from the `impl`. Find that `impl` so that we can point @@ -336,7 +341,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// Report an error because the universal region `fr` was required to outlive /// `outlived_fr` but it is not known to do so. For example: /// - /// ```compile_fail,E0312 + /// ```compile_fail /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } /// ``` /// @@ -350,16 +355,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ) { debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); - let BlameConstraint { category, cause, variance_info, from_closure: _ } = - self.regioncx.best_blame_constraint(&self.body, fr, fr_origin, |r| { + let (blame_constraint, extra_info) = + self.regioncx.best_blame_constraint(fr, fr_origin, |r| { self.regioncx.provides_universal_region(r, fr, outlived_fr) }); + let BlameConstraint { category, cause, variance_info, .. } = blame_constraint; debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info); // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { - let nice = NiceRegionError::new_from_span(self.infcx, cause.span, o, f); + let infer_err = self.infcx.err_ctxt(); + let nice = NiceRegionError::new_from_span(&infer_err, cause.span, o, f); if let Some(diag) = nice.try_report_from_nll() { self.buffer_error(diag); return; @@ -462,6 +469,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } + for extra in extra_info { + match extra { + ExtraConstraintInfo::PlaceholderFromPredicate(span) => { + diag.span_note(span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime")); + } + } + } + self.buffer_error(diag); } @@ -488,12 +503,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let ErrorConstraintInfo { outlived_fr, span, .. } = errci; - let mut diag = self - .infcx - .tcx - .sess - .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body"); - let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; if let ty::Opaque(def_id, _) = *output_ty.kind() { output_ty = self.infcx.tcx.type_of(def_id) @@ -501,19 +510,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { debug!("report_fnmut_error: output_ty={:?}", output_ty); - let message = match output_ty.kind() { - ty::Closure(_, _) => { - "returns a closure that contains a reference to a captured variable, which then \ - escapes the closure body" - } - ty::Adt(def, _) if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did()) => { - "returns an `async` block that contains a reference to a captured variable, which then \ - escapes the closure body" - } - _ => "returns a reference to a captured variable which escapes the closure body", + let err = FnMutError { + span: *span, + ty_err: match output_ty.kind() { + ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span }, + ty::Adt(def, _) + if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did()) => + { + FnMutReturnTypeErr::ReturnAsyncBlock { span: *span } + } + _ => FnMutReturnTypeErr::ReturnRef { span: *span }, + }, }; - diag.span_label(*span, message); + let mut diag = self.infcx.tcx.sess.create_err(err); if let ReturnConstraint::ClosureUpvar(upvar_field) = kind { let def_id = match self.regioncx.universal_regions().defining_ty { @@ -532,20 +542,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap(); let upvar_def_span = self.infcx.tcx.hir().span(def_hir); let upvar_span = upvars_map.get(&def_hir).unwrap().span; - diag.span_label(upvar_def_span, "variable defined here"); - diag.span_label(upvar_span, "variable captured here"); + diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span }); + diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span }); } } if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() { - diag.span_label(fr_span, "inferred to be a `FnMut` closure"); + diag.subdiagnostic(VarHereDenote::FnMutInferred { span: fr_span }); } - diag.note( - "`FnMut` closures only have access to their captured variables while they are \ - executing...", - ); - diag.note("...therefore, they cannot allow references to captured variables to escape"); + self.suggest_move_on_borrowing_closure(&mut diag); diag } @@ -562,6 +568,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// LL | ref_obj(x) /// | ^^^^^^^^^^ `x` escapes the function body here /// ``` + #[instrument(level = "debug", skip(self))] fn report_escaping_data_error( &self, errci: &ErrorConstraintInfo<'tcx>, @@ -680,42 +687,37 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .. } = errci; - let mut diag = - self.infcx.tcx.sess.struct_span_err(*span, "lifetime may not live long enough"); - let (_, mir_def_name) = self.infcx.tcx.article_and_description(self.mir_def_id().to_def_id()); + 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(); 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); - match (category, outlived_fr_is_local, fr_is_local) { - (ConstraintCategory::Return(_), true, _) => { - diag.span_label( - *span, - format!( - "{mir_def_name} was supposed to return data with lifetime `{outlived_fr_name}` but it is returning \ - data with lifetime `{fr_name}`", - ), - ); - } - _ => { - diag.span_label( - *span, - format!( - "{}requires that `{}` must outlive `{}`", - category.description(), - fr_name, - outlived_fr_name, - ), - ); - } - } + let err_category = match (category, outlived_fr_is_local, fr_is_local) { + (ConstraintCategory::Return(_), true, _) => LifetimeReturnCategoryErr::WrongReturn { + span: *span, + mir_def_name, + outlived_fr_name, + fr_name: &fr_name, + }, + _ => LifetimeReturnCategoryErr::ShortReturn { + span: *span, + category_desc: category.description(), + free_region_name: &fr_name, + outlived_fr_name, + }, + }; + + diag.subdiagnostic(err_category); self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr); self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr); + self.suggest_move_on_borrowing_closure(&mut diag); diag } @@ -783,7 +785,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn maybe_suggest_constrain_dyn_trait_impl( &self, - diag: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + diag: &mut Diagnostic, f: Region<'tcx>, o: Region<'tcx>, category: &ConstraintCategory<'tcx>, @@ -860,7 +862,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ident.span, "calling this method introduces the `impl`'s 'static` requirement", ); - err.span_note(multi_span, "the used `impl` has a `'static` requirement"); + err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span }); err.span_suggestion_verbose( span.shrink_to_hi(), "consider relaxing the implicit `'static` requirement", @@ -901,4 +903,46 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag); } + + fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) { + let map = self.infcx.tcx.hir(); + let body_id = map.body_owned_by(self.mir_def_id()); + let expr = &map.body(body_id).value; + let mut closure_span = None::<rustc_span::Span>; + match expr.kind { + hir::ExprKind::MethodCall(.., args, _) => { + for arg in args { + if let hir::ExprKind::Closure(hir::Closure { + capture_clause: hir::CaptureBy::Ref, + .. + }) = arg.kind + { + closure_span = Some(arg.span.shrink_to_lo()); + break; + } + } + } + hir::ExprKind::Block(blk, _) => { + if let Some(ref expr) = blk.expr { + // only when the block is a closure + if let hir::ExprKind::Closure(hir::Closure { + capture_clause: hir::CaptureBy::Ref, + .. + }) = expr.kind + { + closure_span = Some(expr.span.shrink_to_lo()); + } + } + } + _ => {} + } + if let Some(closure_span) = closure_span { + diag.span_suggestion_verbose( + closure_span, + "consider adding 'move' keyword before the nested closure", + "move ", + Applicability::MaybeIncorrect, + ); + } + } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index a87e8bd5b..c044dbaba 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -251,7 +251,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { .or_else(|| self.give_name_if_anonymous_region_appears_in_upvars(fr)) .or_else(|| self.give_name_if_anonymous_region_appears_in_output(fr)) .or_else(|| self.give_name_if_anonymous_region_appears_in_yield_ty(fr)) - .or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr)); + .or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr)) + .or_else(|| self.give_name_if_anonymous_region_appears_in_arg_position_impl_trait(fr)); if let Some(ref value) = value { self.region_names.try_borrow_mut().unwrap().insert(fr, value.clone()); @@ -265,7 +266,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// *user* has a name for. In that case, we'll be able to map /// `fr` to a `Region<'tcx>`, and that region will be one of /// named variants. - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> { let error_region = self.to_error_region(fr)?; @@ -357,11 +358,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::BoundRegionKind::BrAnon(_) => None, }, - ty::ReLateBound(..) - | ty::ReVar(..) - | ty::RePlaceholder(..) - | ty::ReEmpty(_) - | ty::ReErased => None, + ty::ReLateBound(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => None, } } @@ -373,7 +370,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// | fn foo(x: &u32) { .. } /// ------- fully elaborated type of `x` is `&'1 u32` /// ``` - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_arguments( &self, fr: RegionVid, @@ -662,7 +659,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// | let x = Some(&22); /// - fully elaborated type of `x` is `Option<&'1 u32>` /// ``` - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option<RegionName> { let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?; let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( @@ -682,7 +679,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// must be a closure since, in a free fn, such an argument would /// have to either also appear in an argument (if using elision) /// or be early bound (named, not in argument). - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> { let tcx = self.infcx.tcx; let hir = tcx.hir(); @@ -711,7 +708,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::AsyncGeneratorKind::Block => " of async block", hir::AsyncGeneratorKind::Closure => " of async closure", hir::AsyncGeneratorKind::Fn => { - let parent_item = hir.get_by_def_id(hir.get_parent_item(mir_hir_id)); + let parent_item = + hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id); let output = &parent_item .fn_decl() .expect("generator lowered from async fn should be in fn") @@ -772,7 +770,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { let hir = self.infcx.tcx.hir(); - let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind else { + let hir::TyKind::OpaqueDef(id, _, _) = hir_ty.kind else { span_bug!( hir_ty.span, "lowered return type of async fn is not OpaqueDef: {:?}", @@ -814,7 +812,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } } - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_yield_ty( &self, fr: RegionVid, @@ -867,20 +865,13 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { }; let tcx = self.infcx.tcx; - let body_parent_did = tcx.opt_parent(self.mir_def_id().to_def_id())?; - if tcx.parent(region.def_id) != body_parent_did - || tcx.def_kind(body_parent_did) != DefKind::Impl - { + let region_parent = tcx.parent(region.def_id); + if tcx.def_kind(region_parent) != DefKind::Impl { return None; } - let mut found = false; - tcx.fold_regions(tcx.type_of(body_parent_did), |r: ty::Region<'tcx>, _| { - if *r == ty::ReEarlyBound(region) { - found = true; - } - r - }); + let found = tcx + .any_free_region_meets(&tcx.type_of(region_parent), |r| *r == ty::ReEarlyBound(region)); Some(RegionName { name: self.synthesize_region_name(), @@ -893,4 +884,92 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ), }) } + + fn give_name_if_anonymous_region_appears_in_arg_position_impl_trait( + &self, + fr: RegionVid, + ) -> Option<RegionName> { + let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { + return None; + }; + if region.has_name() { + return None; + }; + + let predicates = self + .infcx + .tcx + .predicates_of(self.body.source.def_id()) + .instantiate_identity(self.infcx.tcx) + .predicates; + + if let Some(upvar_index) = self + .regioncx + .universal_regions() + .defining_ty + .upvar_tys() + .position(|ty| self.any_param_predicate_mentions(&predicates, ty, region)) + { + let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( + self.infcx.tcx, + &self.upvars, + upvar_index, + ); + let region_name = self.synthesize_region_name(); + + Some(RegionName { + name: region_name, + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), + }) + } else if let Some(arg_index) = self + .regioncx + .universal_regions() + .unnormalized_input_tys + .iter() + .position(|ty| self.any_param_predicate_mentions(&predicates, *ty, region)) + { + let (arg_name, arg_span) = self.regioncx.get_argument_name_and_span_for_region( + self.body, + &self.local_names, + arg_index, + ); + let region_name = self.synthesize_region_name(); + + Some(RegionName { + name: region_name, + source: RegionNameSource::AnonRegionFromArgument( + RegionNameHighlight::CannotMatchHirTy(arg_span, arg_name?.to_string()), + ), + }) + } else { + None + } + } + + fn any_param_predicate_mentions( + &self, + predicates: &[ty::Predicate<'tcx>], + ty: Ty<'tcx>, + region: ty::EarlyBoundRegion, + ) -> bool { + let tcx = self.infcx.tcx; + ty.walk().any(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Param(_) = ty.kind() + { + predicates.iter().any(|pred| { + match pred.kind().skip_binder() { + ty::PredicateKind::Trait(data) if data.self_ty() == ty => {} + ty::PredicateKind::Projection(data) if data.projection_ty.self_ty() == ty => {} + _ => return false, + } + tcx.any_free_region_meets(pred, |r| { + *r == ty::ReEarlyBound(region) + }) + }) + } else { + false + } + }) + } } diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index ec521b1cf..3157f861d 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -1,6 +1,6 @@ use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{BasicBlock, Body, Location, Place, Rvalue}; +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}; @@ -63,23 +63,24 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { StatementKind::FakeRead(box (_, _)) => { // Only relevant for initialized/liveness/safety checks. } - StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { + StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => { + self.consume_operand(location, op); + } + StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { ref src, ref dst, ref count, - }) => { + })) => { self.consume_operand(location, src); self.consume_operand(location, dst); self.consume_operand(location, count); } - StatementKind::Nop + // Only relevant for mir typeck + StatementKind::AscribeUserType(..) + // Doesn't have any language semantics | StatementKind::Coverage(..) - | StatementKind::AscribeUserType(..) - | StatementKind::Retag { .. } - | StatementKind::StorageLive(..) => { - // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant - // to borrow check. - } + // Does not actually affect borrowck + | StatementKind::StorageLive(..) => {} StatementKind::StorageDead(local) => { self.access_place( location, @@ -88,7 +89,10 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { LocalMutationIsAllowed::Yes, ); } - StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { + StatementKind::Nop + | StatementKind::Retag { .. } + | StatementKind::Deinit(..) + | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 3d8b07382..abfe253d4 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -3,7 +3,6 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] #![feature(let_chains)] -#![feature(let_else)] #![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -19,15 +18,15 @@ extern crate tracing; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::dominators::Dominators; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::ChunkedBitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::{ - traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, - PlaceRef, VarDebugInfoContents, + traversal, Body, ClearCrossCrate, Local, Location, Mutability, NonDivergingIntrinsic, Operand, + Place, PlaceElem, PlaceRef, VarDebugInfoContents, }; use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; @@ -51,6 +50,8 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveE use rustc_mir_dataflow::Analysis; use rustc_mir_dataflow::MoveDataParamEnv; +use crate::session_diagnostics::VarNeedNotMut; + use self::diagnostics::{AccessKind, RegionName}; use self::location::LocationTable; use self::prefixes::PrefixSet; @@ -130,14 +131,11 @@ fn mir_borrowck<'tcx>( debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id())); let hir_owner = tcx.hir().local_def_id_to_hir_id(def.did).owner; - let opt_closure_req = tcx - .infer_ctxt() - .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner)) - .enter(|infcx| { - let input_body: &Body<'_> = &input_body.borrow(); - let promoted: &IndexVec<_, _> = &promoted.borrow(); - do_mir_borrowck(&infcx, input_body, promoted, false).0 - }); + let infcx = + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)).build(); + let input_body: &Body<'_> = &input_body.borrow(); + let promoted: &IndexVec<_, _> = &promoted.borrow(); + let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, false).0; debug!("mir_borrowck done"); tcx.arena.alloc(opt_closure_req) @@ -149,8 +147,8 @@ fn mir_borrowck<'tcx>( /// region ids on which the borrow checking was performed together with Polonius /// facts. #[instrument(skip(infcx, input_body, input_promoted), fields(id=?input_body.source.with_opt_param().as_local().unwrap()), level = "debug")] -fn do_mir_borrowck<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +fn do_mir_borrowck<'tcx>( + infcx: &InferCtxt<'tcx>, input_body: &Body<'tcx>, input_promoted: &IndexVec<Promoted, Body<'tcx>>, return_body_with_facts: bool, @@ -425,17 +423,9 @@ fn do_mir_borrowck<'a, 'tcx>( continue; } - tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| { - let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); - lint.build("variable does not need to be mutable") - .span_suggestion_short( - mut_span, - "remove this `mut`", - "", - Applicability::MachineApplicable, - ) - .emit(); - }) + let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); + + tcx.emit_spanned_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span }) } let tainted_by_errors = mbcx.emit_errors(); @@ -481,7 +471,7 @@ pub struct BodyWithBorrowckFacts<'tcx> { } struct MirBorrowckCtxt<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, param_env: ParamEnv<'tcx>, body: &'cx Body<'tcx>, move_data: &'cx MoveData<'tcx>, @@ -597,22 +587,19 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx flow_state, ); } - StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { - .. - }) => { - span_bug!( + StatementKind::Intrinsic(box ref kind) => match kind { + NonDivergingIntrinsic::Assume(op) => self.consume_operand(location, (op, span), flow_state), + NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!( span, "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics", ) } - StatementKind::Nop + // Only relevant for mir typeck + StatementKind::AscribeUserType(..) + // Doesn't have any language semantics | StatementKind::Coverage(..) - | StatementKind::AscribeUserType(..) - | StatementKind::Retag { .. } - | StatementKind::StorageLive(..) => { - // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant - // to borrow check. - } + // Does not actually affect borrowck + | StatementKind::StorageLive(..) => {} StatementKind::StorageDead(local) => { self.access_place( location, @@ -622,7 +609,10 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx flow_state, ); } - StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { + StatementKind::Nop + | StatementKind::Retag { .. } + | StatementKind::Deinit(..) + | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") } } @@ -982,6 +972,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + #[instrument(level = "debug", skip(self, flow_state))] fn check_access_for_conflict( &mut self, location: Location, @@ -990,11 +981,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { rw: ReadOrWrite, flow_state: &Flows<'cx, 'tcx>, ) -> bool { - debug!( - "check_access_for_conflict(location={:?}, place_span={:?}, sd={:?}, rw={:?})", - location, place_span, sd, rw, - ); - let mut error_reported = false; let tcx = self.infcx.tcx; let body = self.body; @@ -1458,13 +1444,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// Checks whether a borrow of this place is invalidated when the function /// exits + #[instrument(level = "debug", skip(self))] fn check_for_invalidation_at_exit( &mut self, location: Location, borrow: &BorrowData<'tcx>, span: Span, ) { - debug!("check_for_invalidation_at_exit({:?})", borrow); let place = borrow.borrowed_place; let mut root_place = PlaceRef { local: place.local, projection: &[] }; @@ -1791,6 +1777,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for (place_base, elem) in place.iter_projections().rev() { match elem { ProjectionElem::Index(_/*operand*/) | + ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | // assigning to P[i] requires P to be valid. ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => @@ -2182,6 +2169,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Downcast(..) => { let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { diff --git a/compiler/rustc_borrowck/src/location.rs b/compiler/rustc_borrowck/src/location.rs index 70a311694..877944d3d 100644 --- a/compiler/rustc_borrowck/src/location.rs +++ b/compiler/rustc_borrowck/src/location.rs @@ -33,7 +33,7 @@ impl LocationTable { pub(crate) fn new(body: &Body<'_>) -> Self { let mut num_points = 0; let statements_before_block = body - .basic_blocks() + .basic_blocks .iter() .map(|block_data| { let v = num_points; @@ -86,8 +86,7 @@ impl LocationTable { let (block, &first_index) = self .statements_before_block .iter_enumerated() - .filter(|(_, first_index)| **first_index <= point_index) - .last() + .rfind(|&(_, &first_index)| first_index <= point_index) .unwrap(); let statement_index = (point_index - first_index) / 2; diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 0961203d7..08fdd28eb 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -55,8 +55,8 @@ pub(crate) struct NllOutput<'tcx> { /// regions (e.g., region parameters) declared on the function. That set will need to be given to /// `compute_regions`. #[instrument(skip(infcx, param_env, body, promoted), level = "debug")] -pub(crate) fn replace_regions_in_mir<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, +pub(crate) fn replace_regions_in_mir<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body: &mut Body<'tcx>, promoted: &mut IndexVec<Promoted, Body<'tcx>>, @@ -155,7 +155,7 @@ fn populate_polonius_move_facts( /// /// This may result in errors being reported. pub(crate) fn compute_regions<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, + infcx: &InferCtxt<'tcx>, universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, promoted: &IndexVec<Promoted, Body<'tcx>>, @@ -318,8 +318,8 @@ pub(crate) fn compute_regions<'cx, 'tcx>( } } -pub(super) fn dump_mir_results<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub(super) fn dump_mir_results<'tcx>( + infcx: &InferCtxt<'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option<ClosureRegionRequirements<'_>>, @@ -368,8 +368,8 @@ pub(super) fn dump_mir_results<'a, 'tcx>( }; } -pub(super) fn dump_annotation<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub(super) fn dump_annotation<'tcx>( + infcx: &InferCtxt<'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option<ClosureRegionRequirements<'_>>, @@ -389,8 +389,9 @@ pub(super) fn dump_annotation<'a, 'tcx>( // viewing the intraprocedural state, the -Zdump-mir output is // better. + 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(body.span, "external requirements"); + let mut err = tcx.sess.diagnostic().span_note_diag(def_span, "external requirements"); regioncx.annotate(tcx, &mut err); @@ -409,7 +410,7 @@ pub(super) fn dump_annotation<'a, 'tcx>( err } else { - let mut err = tcx.sess.diagnostic().span_note_diag(body.span, "no external requirements"); + let mut err = tcx.sess.diagnostic().span_note_diag(def_span, "no external requirements"); regioncx.annotate(tcx, &mut err); err diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 97335fd0d..0e71efd6f 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -44,6 +44,7 @@ pub(crate) fn places_conflict<'tcx>( /// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime /// array indices, for example) should be interpreted - this depends on what the caller wants in /// order to make the conservative choice and preserve soundness. +#[instrument(level = "debug", skip(tcx, body))] pub(super) fn borrow_conflicts_with_place<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -53,11 +54,6 @@ pub(super) fn borrow_conflicts_with_place<'tcx>( access: AccessDepth, bias: PlaceConflictBias, ) -> bool { - debug!( - "borrow_conflicts_with_place({:?}, {:?}, {:?}, {:?})", - borrow_place, access_place, access, bias, - ); - // This Local/Local case is handled by the more general code below, but // it's so common that it's a speed win to check for it first. if let Some(l1) = borrow_place.as_local() && let Some(l2) = access_place.as_local() { @@ -140,10 +136,9 @@ fn place_components_conflict<'tcx>( for (i, (borrow_c, &access_c)) in iter::zip(borrow_place.projection, access_place.projection).enumerate() { - debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c); - let borrow_proj_base = &borrow_place.projection[..i]; + debug!(?borrow_c, ?access_c); - debug!("borrow_conflicts_with_place: access_c = {:?}", access_c); + let borrow_proj_base = &borrow_place.projection[..i]; // Borrow and access path both have more components. // @@ -180,7 +175,7 @@ fn place_components_conflict<'tcx>( // idea, at least for now, so just give up and // report a conflict. This is unsafe code anyway so // the user could always use raw pointers. - debug!("borrow_conflicts_with_place: arbitrary -> conflict"); + debug!("arbitrary -> conflict"); return true; } Overlap::EqualOrDisjoint => { @@ -189,7 +184,7 @@ fn place_components_conflict<'tcx>( Overlap::Disjoint => { // We have proven the borrow disjoint - further // projections will remain disjoint. - debug!("borrow_conflicts_with_place: disjoint"); + debug!("disjoint"); return false; } } @@ -255,6 +250,7 @@ fn place_components_conflict<'tcx>( | (ProjectionElem::Index { .. }, _, _) | (ProjectionElem::ConstantIndex { .. }, _, _) | (ProjectionElem::Subslice { .. }, _, _) + | (ProjectionElem::OpaqueCast { .. }, _, _) | (ProjectionElem::Downcast { .. }, _, _) => { // Recursive case. This can still be disjoint on a // further iteration if this a shallow access and @@ -322,6 +318,17 @@ fn place_projection_conflict<'tcx>( debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); Overlap::EqualOrDisjoint } + (ProjectionElem::OpaqueCast(v1), ProjectionElem::OpaqueCast(v2)) => { + if v1 == v2 { + // same type - recur. + debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE"); + Overlap::EqualOrDisjoint + } else { + // Different types. Disjoint! + debug!("place_element_conflict: DISJOINT-OPAQUE"); + Overlap::Disjoint + } + } (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => { if f1 == f2 { // same field (e.g., `a.y` vs. `a.y`) - recur. @@ -525,6 +532,7 @@ fn place_projection_conflict<'tcx>( | ProjectionElem::Field(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), _, diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index bdf2becb7..2b50cbac9 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -81,6 +81,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { cursor = cursor_base; diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 2894c6d29..8b63294fb 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -15,7 +15,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::mir::{ Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - ConstraintCategory, Local, Location, ReturnConstraint, + ConstraintCategory, Local, Location, ReturnConstraint, TerminatorKind, }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; @@ -135,7 +135,6 @@ pub struct RegionInferenceContext<'tcx> { /// adds a new lower bound to the SCC it is analyzing: so you wind up /// with `'R: 'O` where `'R` is the pick-region and `'O` is the /// minimal viable option. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub(crate) struct AppliedMemberConstraint { /// The SCC that was affected. (The "member region".) /// @@ -246,6 +245,11 @@ enum Trace<'tcx> { NotVisited, } +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ExtraConstraintInfo { + PlaceholderFromPredicate(Span), +} + impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N @@ -561,7 +565,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { #[instrument(skip(self, infcx, body, polonius_output), level = "debug")] pub(super) fn solve( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, polonius_output: Option<Rc<PoloniusOutput>>, @@ -591,13 +595,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { // constraints were too strong, and if so, emit or propagate those errors. if infcx.tcx.sess.opts.unstable_opts.polonius { self.check_polonius_subset_errors( - body, outlives_requirements.as_mut(), &mut errors_buffer, polonius_output.expect("Polonius output is unavailable despite `-Z polonius`"), ); } else { - self.check_universal_regions(body, outlives_requirements.as_mut(), &mut errors_buffer); + self.check_universal_regions(outlives_requirements.as_mut(), &mut errors_buffer); } if errors_buffer.is_empty() { @@ -832,7 +835,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 'a`. See `TypeTest` for more details. fn check_type_tests( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>, @@ -920,7 +923,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { #[instrument(level = "debug", skip(self, infcx, propagated_outlives_requirements))] fn try_promote_type_test( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, type_test: &TypeTest<'tcx>, @@ -1033,7 +1036,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { #[instrument(level = "debug", skip(self, infcx))] fn try_promote_type_test_subject( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option<ClosureOutlivesSubject<'tcx>> { let tcx = infcx.tcx; @@ -1139,7 +1142,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// include the CFG anyhow. /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding /// a result `'y`. - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] pub(crate) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid { debug!(r = %self.region_value_str(r)); @@ -1151,8 +1154,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { lub = self.universal_region_relations.postdom_upper_bound(lub, ur); } - debug!(?lub); - lub } @@ -1167,8 +1168,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Therefore, this method should only be used in diagnostic code, /// where displaying *some* named universal region is better than /// falling back to 'static. + #[instrument(level = "debug", skip(self))] pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid { - debug!("approx_universal_upper_bound(r={:?}={})", r, self.region_value_str(r)); + debug!("{}", self.region_value_str(r)); // Find the smallest universal region that contains all other // universal regions within `region`. @@ -1177,7 +1179,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let static_r = self.universal_regions.fr_static; for ur in self.scc_values.universal_regions_outlived_by(r_scc) { let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur); - debug!("approx_universal_upper_bound: ur={:?} lub={:?} new_lub={:?}", ur, lub, new_lub); + debug!(?ur, ?lub, ?new_lub); // The upper bound of two non-static regions is static: this // means we know nothing about the relationship between these // two regions. Pick a 'better' one to use when constructing @@ -1201,7 +1203,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - debug!("approx_universal_upper_bound: r={:?} lub={:?}", r, lub); + debug!(?r, ?lub); lub } @@ -1210,7 +1212,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `point`. fn eval_verify_bound( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, generic_ty: Ty<'tcx>, @@ -1260,7 +1262,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn eval_if_eq( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, generic_ty: Ty<'tcx>, lower_bound: RegionVid, @@ -1332,15 +1334,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // Evaluate whether `sup_region: sub_region`. - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { debug!( - "eval_outlives: sup_region's value = {:?} universal={:?}", + "sup_region's value = {:?} universal={:?}", self.region_value_str(sup_region), self.universal_regions.is_universal_region(sup_region), ); debug!( - "eval_outlives: sub_region's value = {:?} universal={:?}", + "sub_region's value = {:?} universal={:?}", self.region_value_str(sub_region), self.universal_regions.is_universal_region(sub_region), ); @@ -1353,7 +1355,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // true if `'sup` outlives static. if !self.universe_compatible(sub_region_scc, sup_region_scc) { debug!( - "eval_outlives: sub universe `{sub_region_scc:?}` is not nameable \ + "sub universe `{sub_region_scc:?}` is not nameable \ by super `{sup_region_scc:?}`, promoting to static", ); @@ -1374,9 +1376,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); if !universal_outlives { - debug!( - "eval_outlives: returning false because sub region contains a universal region not present in super" - ); + debug!("sub region contains a universal region not present in super"); return false; } @@ -1385,22 +1385,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { if self.universal_regions.is_universal_region(sup_region) { // Micro-opt: universal regions contain all points. - debug!( - "eval_outlives: returning true because super is universal and hence contains all points" - ); + debug!("super is universal and hence contains all points"); return true; } - let result = self.scc_values.contains_points(sup_region_scc, sub_region_scc); - debug!("returning {} because of comparison between points in sup/sub", result); - result + debug!("comparison between points in sup/sub"); + + self.scc_values.contains_points(sup_region_scc, sub_region_scc) } /// Once regions have been propagated, this method is used to see /// whether any of the constraints were too strong. In particular, /// we want to check for a case where a universally quantified /// region exceeded its bounds. Consider: - /// ```compile_fail,E0312 + /// ```compile_fail /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } /// ``` /// In this case, returning `x` requires `&'a u32 <: &'b u32` @@ -1415,7 +1413,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// report them as errors. fn check_universal_regions( &self, - body: &Body<'tcx>, mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>, errors_buffer: &mut RegionErrors<'tcx>, ) { @@ -1426,7 +1423,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { // they did not grow too large, accumulating any requirements // for our caller into the `outlives_requirements` vector. self.check_universal_region( - body, fr, &mut propagated_outlives_requirements, errors_buffer, @@ -1455,7 +1451,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// <https://smallcultfollowing.com/babysteps/blog/2019/01/17/polonius-and-region-errors/> /// /// In the canonical example - /// ```compile_fail,E0312 + /// ```compile_fail /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } /// ``` /// returning `x` requires `&'a u32 <: &'b u32` and hence we establish (transitively) a @@ -1467,7 +1463,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// report them as errors. fn check_polonius_subset_errors( &self, - body: &Body<'tcx>, mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>, errors_buffer: &mut RegionErrors<'tcx>, polonius_output: Rc<PoloniusOutput>, @@ -1514,7 +1509,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { let propagated = self.try_propagate_universal_region_error( *longer_fr, *shorter_fr, - body, &mut propagated_outlives_requirements, ); if propagated == RegionRelationCheckResult::Error { @@ -1554,13 +1548,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Things that are to be propagated are accumulated into the /// `outlives_requirements` vector. - #[instrument( - skip(self, body, propagated_outlives_requirements, errors_buffer), - level = "debug" - )] + #[instrument(skip(self, propagated_outlives_requirements, errors_buffer), level = "debug")] fn check_universal_region( &self, - body: &Body<'tcx>, longer_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>, errors_buffer: &mut RegionErrors<'tcx>, @@ -1583,7 +1573,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let RegionRelationCheckResult::Error = self.check_universal_region_relation( longer_fr, representative, - body, propagated_outlives_requirements, ) { errors_buffer.push(RegionErrorKind::RegionError { @@ -1603,7 +1592,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let RegionRelationCheckResult::Error = self.check_universal_region_relation( longer_fr, shorter_fr, - body, propagated_outlives_requirements, ) { // We only report the first region error. Subsequent errors are hidden so as @@ -1628,7 +1616,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, longer_fr: RegionVid, shorter_fr: RegionVid, - body: &Body<'tcx>, propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>, ) -> RegionRelationCheckResult { // If it is known that `fr: o`, carry on. @@ -1644,7 +1631,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.try_propagate_universal_region_error( longer_fr, shorter_fr, - body, propagated_outlives_requirements, ) } @@ -1656,7 +1642,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, longer_fr: RegionVid, shorter_fr: RegionVid, - body: &Body<'tcx>, propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>, ) -> RegionRelationCheckResult { if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { @@ -1668,7 +1653,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus); let blame_span_category = self.find_outlives_blame_span( - body, longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr, @@ -1734,7 +1718,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn check_member_constraints( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, errors_buffer: &mut RegionErrors<'tcx>, ) { let member_constraints = self.member_constraints.clone(); @@ -1822,50 +1806,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { pub(crate) fn retrieve_closure_constraint_info( &self, - _body: &Body<'tcx>, - constraint: &OutlivesConstraint<'tcx>, - ) -> BlameConstraint<'tcx> { - let loc = match constraint.locations { - Locations::All(span) => { - return BlameConstraint { - category: constraint.category, - from_closure: false, - cause: ObligationCause::dummy_with_span(span), - variance_info: constraint.variance_info, - }; + constraint: OutlivesConstraint<'tcx>, + ) -> Option<(ConstraintCategory<'tcx>, Span)> { + match constraint.locations { + Locations::All(_) => None, + Locations::Single(loc) => { + self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub)).copied() } - Locations::Single(loc) => loc, - }; - - let opt_span_category = - self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub)); - opt_span_category - .map(|&(category, span)| BlameConstraint { - category, - from_closure: true, - cause: ObligationCause::dummy_with_span(span), - variance_info: constraint.variance_info, - }) - .unwrap_or(BlameConstraint { - category: constraint.category, - from_closure: false, - cause: ObligationCause::dummy_with_span(constraint.span), - variance_info: constraint.variance_info, - }) + } } /// Finds a good `ObligationCause` to blame for the fact that `fr1` outlives `fr2`. pub(crate) fn find_outlives_blame_span( &self, - body: &Body<'tcx>, fr1: RegionVid, fr1_origin: NllRegionVariableOrigin, fr2: RegionVid, ) -> (ConstraintCategory<'tcx>, ObligationCause<'tcx>) { - let BlameConstraint { category, cause, .. } = - self.best_blame_constraint(body, fr1, fr1_origin, |r| { - self.provides_universal_region(r, fr1, fr2) - }); + let BlameConstraint { category, cause, .. } = self + .best_blame_constraint(fr1, fr1_origin, |r| self.provides_universal_region(r, fr1, fr2)) + .0; (category, cause) } @@ -1970,7 +1930,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Finds some region R such that `fr1: R` and `R` is live at `elem`. - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { trace!(scc = ?self.constraint_sccs.scc(fr1)); trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]); @@ -2048,23 +2008,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// creating a constraint path that forces `R` to outlive /// `from_region`, and then finding the best choices within that /// path to blame. + #[instrument(level = "debug", skip(self, target_test))] pub(crate) fn best_blame_constraint( &self, - body: &Body<'tcx>, from_region: RegionVid, from_region_origin: NllRegionVariableOrigin, target_test: impl Fn(RegionVid) -> bool, - ) -> BlameConstraint<'tcx> { - debug!( - "best_blame_constraint(from_region={:?}, from_region_origin={:?})", - from_region, from_region_origin - ); - + ) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) { // Find all paths let (path, target_region) = self.find_constraint_paths_between_regions(from_region, target_test).unwrap(); debug!( - "best_blame_constraint: path={:#?}", + "path={:#?}", path.iter() .map(|c| format!( "{:?} ({:?}: {:?})", @@ -2075,6 +2030,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { .collect::<Vec<_>>() ); + let mut extra_info = vec![]; + for constraint in path.iter() { + let outlived = constraint.sub; + let Some(origin) = self.var_infos.get(outlived) else { continue; }; + let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(p)) = origin.origin else { continue; }; + debug!(?constraint, ?p); + let ConstraintCategory::Predicate(span) = constraint.category else { continue; }; + extra_info.push(ExtraConstraintInfo::PlaceholderFromPredicate(span)); + // We only want to point to one + break; + } + // We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint. // Instead, we use it to produce an improved `ObligationCauseCode`. // FIXME - determine what we should do if we encounter multiple `ConstraintCategory::Predicate` @@ -2100,23 +2067,33 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut categorized_path: Vec<BlameConstraint<'tcx>> = path .iter() .map(|constraint| { - if constraint.category == ConstraintCategory::ClosureBounds { - self.retrieve_closure_constraint_info(body, &constraint) - } else { - BlameConstraint { - category: constraint.category, - from_closure: false, - cause: ObligationCause::new( - constraint.span, - CRATE_HIR_ID, - cause_code.clone(), - ), - variance_info: constraint.variance_info, - } + let (category, span, from_closure, cause_code) = + if constraint.category == ConstraintCategory::ClosureBounds { + if let Some((category, span)) = + self.retrieve_closure_constraint_info(*constraint) + { + (category, span, true, ObligationCauseCode::MiscObligation) + } else { + ( + constraint.category, + constraint.span, + false, + ObligationCauseCode::MiscObligation, + ) + } + } else { + (constraint.category, constraint.span, false, cause_code.clone()) + }; + BlameConstraint { + category, + from_closure, + cause: ObligationCause::new(span, CRATE_HIR_ID, cause_code), + variance_info: constraint.variance_info, + outlives_constraint: *constraint, } }) .collect(); - debug!("best_blame_constraint: categorized_path={:#?}", categorized_path); + debug!("categorized_path={:#?}", categorized_path); // To find the best span to cite, we first try to look for the // final constraint that is interesting and where the `sup` is @@ -2214,10 +2191,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let best_choice = if blame_source { range.rev().find(find_region) } else { range.find(find_region) }; - debug!( - "best_blame_constraint: best_choice={:?} blame_source={}", - best_choice, blame_source - ); + debug!(?best_choice, ?blame_source, ?extra_info); if let Some(i) = best_choice { if let Some(next) = categorized_path.get(i + 1) { @@ -2226,7 +2200,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { { // The return expression is being influenced by the return type being // impl Trait, point at the return type and not the return expr. - return next.clone(); + return (next.clone(), extra_info); } } @@ -2246,7 +2220,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - return categorized_path[i].clone(); + return (categorized_path[i].clone(), extra_info); } // If that search fails, that is.. unusual. Maybe everything @@ -2254,14 +2228,35 @@ impl<'tcx> RegionInferenceContext<'tcx> { // appears to be the most interesting point to report to the // user via an even more ad-hoc guess. categorized_path.sort_by(|p0, p1| p0.category.cmp(&p1.category)); - debug!("best_blame_constraint: sorted_path={:#?}", categorized_path); + debug!("sorted_path={:#?}", categorized_path); - categorized_path.remove(0) + (categorized_path.remove(0), extra_info) } pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { self.universe_causes[&universe].clone() } + + /// Tries to find the terminator of the loop in which the region 'r' resides. + /// Returns the location of the terminator if found. + pub(crate) fn find_loop_terminator_location( + &self, + r: RegionVid, + body: &Body<'_>, + ) -> Option<Location> { + let scc = self.constraint_sccs.scc(r.to_region_vid()); + let locations = self.scc_values.locations_outlived_by(scc); + for location in locations { + let bb = &body[location.block]; + if let Some(terminator) = &bb.terminator { + // terminator of a loop should be TerminatorKind::FalseUnwind + if let TerminatorKind::FalseUnwind { .. } = terminator.kind { + return Some(location); + } + } + } + None + } } impl<'tcx> RegionDefinition<'tcx> { @@ -2338,7 +2333,13 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx outlives_requirement={:?}", region, outlived_region, outlives_requirement, ); - ty::Binder::dummy(ty::OutlivesPredicate(region.into(), outlived_region)) + ( + ty::Binder::dummy(ty::OutlivesPredicate( + region.into(), + outlived_region, + )), + ConstraintCategory::BoringNoLocation, + ) } ClosureOutlivesSubject::Ty(ty) => { @@ -2348,7 +2349,10 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx outlives_requirement={:?}", ty, outlived_region, outlives_requirement, ); - ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), outlived_region)) + ( + ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), outlived_region)), + ConstraintCategory::BoringNoLocation, + ) } } }) @@ -2362,4 +2366,5 @@ pub struct BlameConstraint<'tcx> { pub from_closure: bool, pub cause: ObligationCause<'tcx>, pub variance_info: ty::VarianceDiagInfo<'tcx>, + pub outlives_constraint: OutlivesConstraint<'tcx>, } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index d6712b6a4..465f353aa 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -2,18 +2,16 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::vec_map::VecMap; use rustc_hir::def_id::LocalDefId; use rustc_hir::OpaqueTyOrigin; -use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_infer::infer::{DefiningAnchor, InferCtxt}; use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; -use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt, TypeFoldable, }; use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::TraitEngineExt as _; use super::RegionInferenceContext; @@ -58,10 +56,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`, /// which has no `external_name` in which case we use `'empty` as the /// region to pass to `infer_opaque_definition_from_instantiation`. - #[instrument(level = "debug", skip(self, infcx))] + #[instrument(level = "debug", skip(self, infcx), ret)] pub(crate) fn infer_opaque_types( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, opaque_ty_decls: VecMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>, ) -> VecMap<LocalDefId, OpaqueHiddenType<'tcx>> { let mut result: VecMap<LocalDefId, OpaqueHiddenType<'tcx>> = VecMap::new(); @@ -107,7 +105,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { .iter() .find(|ur_vid| self.eval_equal(vid, **ur_vid)) .and_then(|ur_vid| self.definitions[*ur_vid].external_name) - .unwrap_or(infcx.tcx.lifetimes.re_root_empty), + .unwrap_or(infcx.tcx.lifetimes.re_erased), _ => region, }); @@ -192,7 +190,7 @@ pub trait InferCtxtExt<'tcx> { ) -> Ty<'tcx>; } -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// Given the fully resolved, instantiated type for an opaque /// type, i.e., the value of an inference variable like C1 or C2 /// (*), computes the "definition type" for an opaque type @@ -227,31 +225,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return self.tcx.ty_error(); } - let OpaqueTypeKey { def_id, substs } = opaque_type_key; - - // Use substs to build up a reverse map from regions to their - // identity mappings. This is necessary because of `impl - // Trait` lifetimes are computed by replacing existing - // lifetimes with 'static and remapping only those used in the - // `impl Trait` return type, resulting in the parameters - // shifting. - let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id()); - debug!(?id_substs); - let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = - substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect(); - debug!("map = {:#?}", map); - - // Convert the type from the function into a type valid outside - // the function, by replacing invalid regions with 'static, - // after producing an error for each of them. - let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new( - self.tcx, - opaque_type_key, - map, - instantiated_ty.ty, - instantiated_ty.span, - )); - debug!(?definition_ty); + let definition_ty = instantiated_ty + .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false, origin) + .ty; if !check_opaque_type_parameter_valid( self.tcx, @@ -264,72 +240,70 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs` // on stable and we'd break that. - if let OpaqueTyOrigin::TyAlias = origin { - // This logic duplicates most of `check_opaque_meets_bounds`. - // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely. - let param_env = self.tcx.param_env(def_id); - let body_id = self.tcx.local_def_id_to_hir_id(def_id); - // HACK This bubble is required for this tests to pass: - // type-alias-impl-trait/issue-67844-nested-opaque.rs - self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter( - move |infcx| { - // Require the hidden type to be well-formed with only the generics of the opaque type. - // Defining use functions may have more bounds than the opaque type, which is ok, as long as the - // hidden type is well formed even without those bounds. - let predicate = - ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into())) - .to_predicate(infcx.tcx); - let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); + let OpaqueTyOrigin::TyAlias = origin else { + return definition_ty; + }; + let def_id = opaque_type_key.def_id; + // This logic duplicates most of `check_opaque_meets_bounds`. + // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely. + let param_env = self.tcx.param_env(def_id); + let body_id = self.tcx.local_def_id_to_hir_id(def_id); + // HACK This bubble is required for this tests to pass: + // type-alias-impl-trait/issue-67844-nested-opaque.rs + let infcx = + self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).build(); + // Require the hidden type to be well-formed with only the generics of the opaque type. + // Defining use functions may have more bounds than the opaque type, which is ok, as long as the + // hidden type is well formed even without those bounds. + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into())) + .to_predicate(infcx.tcx); + let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); + + let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id()); - // Require that the hidden type actually fulfills all the bounds of the opaque type, even without - // the bounds that the function supplies. - match infcx.register_hidden_type( - OpaqueTypeKey { def_id, substs: id_substs }, - ObligationCause::misc(instantiated_ty.span, body_id), - param_env, + // Require that the hidden type actually fulfills all the bounds of the opaque type, even without + // the bounds that the function supplies. + let opaque_ty = self.tcx.mk_opaque(def_id.to_def_id(), id_substs); + match infcx + .at(&ObligationCause::misc(instantiated_ty.span, body_id), param_env) + .eq(opaque_ty, definition_ty) + { + Ok(infer_ok) => { + for obligation in infer_ok.obligations { + fulfillment_cx.register_predicate_obligation(&infcx, obligation); + } + } + Err(err) => { + infcx + .err_ctxt() + .report_mismatched_types( + &ObligationCause::misc(instantiated_ty.span, body_id), + opaque_ty, definition_ty, - origin, - ) { - Ok(infer_ok) => { - for obligation in infer_ok.obligations { - fulfillment_cx.register_predicate_obligation(&infcx, obligation); - } - } - Err(err) => { - infcx - .report_mismatched_types( - &ObligationCause::misc(instantiated_ty.span, body_id), - self.tcx.mk_opaque(def_id.to_def_id(), id_substs), - definition_ty, - err, - ) - .emit(); - } - } + err, + ) + .emit(); + } + } - fulfillment_cx.register_predicate_obligation( - &infcx, - Obligation::misc(instantiated_ty.span, body_id, param_env, predicate), - ); + fulfillment_cx.register_predicate_obligation( + &infcx, + Obligation::misc(instantiated_ty.span, body_id, param_env, predicate), + ); - // Check that all obligations are satisfied by the implementation's - // version. - let errors = fulfillment_cx.select_all_or_error(&infcx); + // Check that all obligations are satisfied by the implementation's + // version. + let errors = fulfillment_cx.select_all_or_error(&infcx); - // This is still required for many(half of the tests in ui/type-alias-impl-trait) - // tests to pass - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + // This is still required for many(half of the tests in ui/type-alias-impl-trait) + // tests to pass + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - if errors.is_empty() { - definition_ty - } else { - infcx.report_fulfillment_errors(&errors, None, false); - self.tcx.ty_error() - } - }, - ) - } else { + if errors.is_empty() { definition_ty + } else { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + self.tcx.ty_error() } } } @@ -425,238 +399,3 @@ fn check_opaque_type_parameter_valid( } true } - -struct ReverseMapper<'tcx> { - tcx: TyCtxt<'tcx>, - - key: ty::OpaqueTypeKey<'tcx>, - map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, - map_missing_regions_to_empty: bool, - - /// initially `Some`, set to `None` once error has been reported - hidden_ty: Option<Ty<'tcx>>, - - /// Span of function being checked. - span: Span, -} - -impl<'tcx> ReverseMapper<'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - key: ty::OpaqueTypeKey<'tcx>, - map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, - hidden_ty: Ty<'tcx>, - span: Span, - ) -> Self { - Self { - tcx, - key, - map, - map_missing_regions_to_empty: false, - hidden_ty: Some(hidden_ty), - span, - } - } - - fn fold_kind_mapping_missing_regions_to_empty( - &mut self, - kind: GenericArg<'tcx>, - ) -> GenericArg<'tcx> { - assert!(!self.map_missing_regions_to_empty); - self.map_missing_regions_to_empty = true; - let kind = kind.fold_with(self); - self.map_missing_regions_to_empty = false; - kind - } - - fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { - assert!(!self.map_missing_regions_to_empty); - kind.fold_with(self) - } -} - -impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - #[instrument(skip(self), level = "debug")] - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - // Ignore bound regions and `'static` regions that appear in the - // type, we only need to remap regions that reference lifetimes - // from the function declaration. - // This would ignore `'r` in a type like `for<'r> fn(&'r u32)`. - ty::ReLateBound(..) | ty::ReStatic => return r, - - // If regions have been erased (by writeback), don't try to unerase - // them. - ty::ReErased => return r, - - // The regions that we expect from borrow checking. - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {} - - ty::ReEmpty(_) | ty::RePlaceholder(_) | ty::ReVar(_) => { - // All of the regions in the type should either have been - // erased by writeback, or mapped back to named regions by - // borrow checking. - bug!("unexpected region kind in opaque type: {:?}", r); - } - } - - let generics = self.tcx().generics_of(self.key.def_id); - match self.map.get(&r.into()).map(|k| k.unpack()) { - Some(GenericArgKind::Lifetime(r1)) => r1, - Some(u) => panic!("region mapped to unexpected kind: {:?}", u), - None if self.map_missing_regions_to_empty => self.tcx.lifetimes.re_root_empty, - None if generics.parent.is_some() => { - if let Some(hidden_ty) = self.hidden_ty.take() { - unexpected_hidden_region_diagnostic( - self.tcx, - self.tcx.def_span(self.key.def_id), - hidden_ty, - r, - self.key, - ) - .emit(); - } - self.tcx.lifetimes.re_root_empty - } - None => { - self.tcx - .sess - .struct_span_err(self.span, "non-defining opaque type use in defining scope") - .span_label( - self.span, - format!( - "lifetime `{}` is part of concrete type but not used in \ - parameter list of the `impl Trait` type alias", - r - ), - ) - .emit(); - - self.tcx().lifetimes.re_static - } - } - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Closure(def_id, substs) => { - // I am a horrible monster and I pray for death. When - // we encounter a closure here, it is always a closure - // from within the function that we are currently - // type-checking -- one that is now being encapsulated - // in an opaque type. Ideally, we would - // go through the types/lifetimes that it references - // and treat them just like we would any other type, - // which means we would error out if we find any - // reference to a type/region that is not in the - // "reverse map". - // - // **However,** in the case of closures, there is a - // somewhat subtle (read: hacky) consideration. The - // problem is that our closure types currently include - // all the lifetime parameters declared on the - // enclosing function, even if they are unused by the - // closure itself. We can't readily filter them out, - // so here we replace those values with `'empty`. This - // can't really make a difference to the rest of the - // compiler; those regions are ignored for the - // outlives relation, and hence don't affect trait - // selection or auto traits, and they are erased - // during codegen. - - let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { - if index < generics.parent_count { - // Accommodate missing regions in the parent kinds... - self.fold_kind_mapping_missing_regions_to_empty(kind) - } else { - // ...but not elsewhere. - self.fold_kind_normally(kind) - } - })); - - self.tcx.mk_closure(def_id, substs) - } - - ty::Generator(def_id, substs, movability) => { - let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { - if index < generics.parent_count { - // Accommodate missing regions in the parent kinds... - self.fold_kind_mapping_missing_regions_to_empty(kind) - } else { - // ...but not elsewhere. - self.fold_kind_normally(kind) - } - })); - - self.tcx.mk_generator(def_id, substs, movability) - } - - ty::Param(param) => { - // Look it up in the substitution list. - match self.map.get(&ty.into()).map(|k| k.unpack()) { - // Found it in the substitution list; replace with the parameter from the - // opaque type. - Some(GenericArgKind::Type(t1)) => t1, - Some(u) => panic!("type mapped to unexpected kind: {:?}", u), - None => { - debug!(?param, ?self.map); - self.tcx - .sess - .struct_span_err( - self.span, - &format!( - "type parameter `{}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias", - ty - ), - ) - .emit(); - - self.tcx().ty_error() - } - } - } - - _ => ty.super_fold_with(self), - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - trace!("checking const {:?}", ct); - // Find a const parameter - match ct.kind() { - ty::ConstKind::Param(..) => { - // Look it up in the substitution list. - match self.map.get(&ct.into()).map(|k| k.unpack()) { - // Found it in the substitution list, replace with the parameter from the - // opaque type. - Some(GenericArgKind::Const(c1)) => c1, - Some(u) => panic!("const mapped to unexpected kind: {:?}", u), - None => { - self.tcx - .sess - .struct_span_err( - self.span, - &format!( - "const parameter `{}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias", - ct - ), - ) - .emit(); - - self.tcx().const_error(ct.ty()) - } - } - } - - _ => ct, - } - } -} diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index c81ef10f7..de20a4bb4 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -25,7 +25,7 @@ impl RegionValueElements { pub(crate) fn new(body: &Body<'_>) -> Self { let mut num_points = 0; let statements_before_block: IndexVec<BasicBlock, usize> = body - .basic_blocks() + .basic_blocks .iter() .map(|block_data| { let v = num_points; @@ -37,7 +37,7 @@ impl RegionValueElements { debug!("RegionValueElements: num_points={:#?}", num_points); let mut basic_blocks = IndexVec::with_capacity(num_points); - for (bb, bb_data) in body.basic_blocks().iter_enumerated() { + for (bb, bb_data) in body.basic_blocks.iter_enumerated() { basic_blocks.extend((0..=bb_data.statements.len()).map(|_| bb)); } diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index 7a8ce621c..f30237690 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -1,6 +1,7 @@ use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::mir::visit::{MutVisitor, TyContext}; +use rustc_middle::mir::Constant; use rustc_middle::mir::{Body, Location, Promoted}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; @@ -9,7 +10,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; /// inference variables, returning the number of variables created. #[instrument(skip(infcx, body, promoted), level = "debug")] pub fn renumber_mir<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, body: &mut Body<'tcx>, promoted: &mut IndexVec<Promoted, Body<'tcx>>, ) { @@ -27,7 +28,7 @@ pub fn renumber_mir<'tcx>( /// Replaces all regions appearing in `value` with fresh inference /// variables. #[instrument(skip(infcx), level = "debug")] -pub fn renumber_regions<'tcx, T>(infcx: &InferCtxt<'_, 'tcx>, value: T) -> T +pub fn renumber_regions<'tcx, T>(infcx: &InferCtxt<'tcx>, value: T) -> T where T: TypeFoldable<'tcx>, { @@ -38,7 +39,7 @@ where } struct NllVisitor<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, } impl<'a, 'tcx> NllVisitor<'a, 'tcx> { @@ -77,7 +78,10 @@ impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> { debug!(?region); } - fn visit_const(&mut self, constant: &mut ty::Const<'tcx>, _location: Location) { - *constant = self.renumber_regions(*constant); + #[instrument(skip(self), level = "debug")] + fn visit_constant(&mut self, constant: &mut Constant<'tcx>, _location: Location) { + let literal = constant.literal; + constant.literal = self.renumber_regions(literal); + debug!("constant: {:#?}", constant); } } diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 895723d44..cff3089c3 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -1,9 +1,12 @@ -use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; +use rustc_errors::{IntoDiagnosticArg, MultiSpan}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::Span; -#[derive(SessionDiagnostic)] -#[error(borrowck::move_unsized, code = "E0161")] +use crate::diagnostics::RegionName; + +#[derive(Diagnostic)] +#[diag(borrowck_move_unsized, code = "E0161")] pub(crate) struct MoveUnsized<'tcx> { pub ty: Ty<'tcx>, #[primary_span] @@ -11,8 +14,8 @@ pub(crate) struct MoveUnsized<'tcx> { pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(borrowck::higher_ranked_lifetime_error)] +#[derive(Diagnostic)] +#[diag(borrowck_higher_ranked_lifetime_error)] pub(crate) struct HigherRankedLifetimeError { #[subdiagnostic] pub cause: Option<HigherRankedErrorCause>, @@ -20,25 +23,128 @@ pub(crate) struct HigherRankedLifetimeError { pub span: Span, } -#[derive(SessionSubdiagnostic)] +#[derive(Subdiagnostic)] pub(crate) enum HigherRankedErrorCause { - #[note(borrowck::could_not_prove)] + #[note(borrowck_could_not_prove)] CouldNotProve { predicate: String }, - #[note(borrowck::could_not_normalize)] + #[note(borrowck_could_not_normalize)] CouldNotNormalize { value: String }, } -#[derive(SessionDiagnostic)] -#[error(borrowck::higher_ranked_subtype_error)] +#[derive(Diagnostic)] +#[diag(borrowck_higher_ranked_subtype_error)] pub(crate) struct HigherRankedSubtypeError { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[error(borrowck::generic_does_not_live_long_enough)] +#[derive(Diagnostic)] +#[diag(borrowck_generic_does_not_live_long_enough)] pub(crate) struct GenericDoesNotLiveLongEnough { pub kind: String, #[primary_span] pub span: Span, } + +#[derive(LintDiagnostic)] +#[diag(borrowck_var_does_not_need_mut)] +pub(crate) struct VarNeedNotMut { + #[suggestion_short(applicability = "machine-applicable", code = "")] + pub span: Span, +} +#[derive(Diagnostic)] +#[diag(borrowck_var_cannot_escape_closure)] +#[note] +#[note(cannot_escape)] +pub(crate) struct FnMutError { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub ty_err: FnMutReturnTypeErr, +} + +#[derive(Subdiagnostic)] +pub(crate) enum VarHereDenote { + #[label(borrowck_var_here_captured)] + Captured { + #[primary_span] + span: Span, + }, + #[label(borrowck_var_here_defined)] + Defined { + #[primary_span] + span: Span, + }, + #[label(borrowck_closure_inferred_mut)] + FnMutInferred { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +pub(crate) enum FnMutReturnTypeErr { + #[label(borrowck_returned_closure_escaped)] + ReturnClosure { + #[primary_span] + span: Span, + }, + #[label(borrowck_returned_async_block_escaped)] + ReturnAsyncBlock { + #[primary_span] + span: Span, + }, + #[label(borrowck_returned_ref_escaped)] + ReturnRef { + #[primary_span] + span: Span, + }, +} + +#[derive(Diagnostic)] +#[diag(borrowck_lifetime_constraints_error)] +pub(crate) struct LifetimeOutliveErr { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +pub(crate) enum LifetimeReturnCategoryErr<'a> { + #[label(borrowck_returned_lifetime_wrong)] + WrongReturn { + #[primary_span] + span: Span, + mir_def_name: &'a str, + outlived_fr_name: RegionName, + fr_name: &'a RegionName, + }, + #[label(borrowck_returned_lifetime_short)] + ShortReturn { + #[primary_span] + span: Span, + category_desc: &'static str, + free_region_name: &'a RegionName, + outlived_fr_name: RegionName, + }, +} + +impl IntoDiagnosticArg for &RegionName { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + format!("{}", self).into_diagnostic_arg() + } +} + +impl IntoDiagnosticArg for RegionName { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + format!("{}", self).into_diagnostic_arg() + } +} + +#[derive(Subdiagnostic)] +pub(crate) enum RequireStaticErr { + #[note(borrowck_used_impl_require_static)] + UsedImpl { + #[primary_span] + multi_span: MultiSpan, + }, +} diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 6cfe5efb6..a581726a1 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -24,8 +24,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// **Any `rustc_infer::infer` operations that might generate region /// constraints should occur within this method so that those /// constraints can be properly localized!** - #[instrument(skip(self, category, op), level = "trace")] - pub(super) fn fully_perform_op<R, Op>( + #[instrument(skip(self, op), level = "trace")] + pub(super) fn fully_perform_op<R: fmt::Debug, Op>( &mut self, locations: Locations, category: ConstraintCategory<'tcx>, @@ -39,6 +39,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx)?; + debug!(?output, ?constraints); + if let Some(data) = constraints { self.push_region_constraints(locations, category, data); } @@ -50,11 +52,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Some(error_info) => error_info.to_universe_info(old_universe), None => UniverseInfo::other(), }; - for u in old_universe..universe { - self.borrowck_context - .constraints - .universe_causes - .insert(u + 1, universe_info.clone()); + for u in (old_universe + 1)..=universe { + self.borrowck_context.constraints.universe_causes.insert(u, universe_info.clone()); } } @@ -69,15 +68,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { where T: TypeFoldable<'tcx>, { + let old_universe = self.infcx.universe(); + let (instantiated, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); - for u in 0..canonical.max_universe.as_u32() { - let info = UniverseInfo::other(); - self.borrowck_context - .constraints - .universe_causes - .insert(ty::UniverseIndex::from_u32(u), info); + for u in (old_universe + 1)..=self.infcx.universe() { + self.borrowck_context.constraints.universe_causes.insert(u, UniverseInfo::other()); } instantiated @@ -90,17 +87,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, category: ConstraintCategory<'tcx>, ) { - self.prove_predicates( - Some(ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate { + self.prove_predicate( + ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: ty::BoundConstness::NotConst, polarity: ty::ImplPolarity::Positive, - }))), + })) + .to_predicate(self.tcx()), locations, category, ); } + #[instrument(level = "debug", skip(self))] pub(super) fn normalize_and_prove_instantiated_predicates( &mut self, // Keep this parameter for now, in case we start using @@ -115,8 +114,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .zip(instantiated_predicates.spans.into_iter()) { debug!(?predicate); - let predicate = self.normalize(predicate, locations); - self.prove_predicate(predicate, locations, ConstraintCategory::Predicate(span)); + let category = ConstraintCategory::Predicate(span); + let predicate = self.normalize_with_category(predicate, locations, category); + self.prove_predicate(predicate, locations, category); } } @@ -152,15 +152,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { }) } - #[instrument(skip(self), level = "debug")] pub(super) fn normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T where T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx, { + self.normalize_with_category(value, location, ConstraintCategory::Boring) + } + + #[instrument(skip(self), level = "debug")] + pub(super) fn normalize_with_category<T>( + &mut self, + value: T, + location: impl NormalizeLocation, + category: ConstraintCategory<'tcx>, + ) -> T + where + T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx, + { let param_env = self.param_env; self.fully_perform_op( location.to_locations(), - ConstraintCategory::Boring, + category, param_env.and(type_op::normalize::Normalize::new(value)), ) .unwrap_or_else(|NoSolution| { diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 167960918..d5bfc2f52 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -6,7 +6,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::TypeVisitable; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{Span, DUMMY_SP}; @@ -19,7 +19,7 @@ use crate::{ }; pub(crate) struct ConstraintConversion<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, tcx: TyCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, /// Each RBP `GK: 'a` is assumed to be true. These encode @@ -43,7 +43,7 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { pub(crate) fn new( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, @@ -86,7 +86,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } - pub(super) fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) { + fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) { debug!("generate: constraints at: {:#?}", self.locations); // Extract out various useful fields we'll need below. @@ -98,34 +98,25 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // region constraints like `for<'a> 'a: 'b`. At some point // when we move to universes, we will, and this assertion // will start to fail. - let ty::OutlivesPredicate(k1, r2) = query_constraint.no_bound_vars().unwrap_or_else(|| { - bug!("query_constraint {:?} contained bound vars", query_constraint,); - }); + let ty::OutlivesPredicate(k1, r2) = + query_constraint.0.no_bound_vars().unwrap_or_else(|| { + bug!("query_constraint {:?} contained bound vars", query_constraint,); + }); + + let constraint_category = query_constraint.1; match k1.unpack() { GenericArgKind::Lifetime(r1) => { let r1_vid = self.to_region_vid(r1); let r2_vid = self.to_region_vid(r2); - self.add_outlives(r1_vid, r2_vid); + self.add_outlives(r1_vid, r2_vid, constraint_category); } - GenericArgKind::Type(mut t1) => { + GenericArgKind::Type(t1) => { // we don't actually use this for anything, but // the `TypeOutlives` code needs an origin. let origin = infer::RelateParamBound(DUMMY_SP, t1, None); - // Placeholder regions need to be converted now because it may - // create new region variables, which can't be done later when - // verifying these bounds. - if t1.has_placeholders() { - t1 = tcx.fold_regions(t1, |r, _| match *r { - ty::RePlaceholder(placeholder) => { - self.constraints.placeholder_region(self.infcx, placeholder) - } - _ => r, - }); - } - TypeOutlives::new( &mut *self, tcx, @@ -133,7 +124,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { Some(implicit_region_bound), param_env, ) - .type_must_outlive(origin, t1, r2); + .type_must_outlive(origin, t1, r2, constraint_category); } GenericArgKind::Const(_) => { @@ -143,6 +134,25 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } + /// Placeholder regions need to be converted eagerly because it may + /// create new region variables, which we must not do when verifying + /// our region bounds. + /// + /// FIXME: This should get removed once higher ranked region obligations + /// are dealt with during trait solving. + fn replace_placeholders_with_nll<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T { + if value.has_placeholders() { + self.tcx.fold_regions(value, |r, _| match *r { + ty::RePlaceholder(placeholder) => { + self.constraints.placeholder_region(self.infcx, placeholder) + } + _ => r, + }) + } else { + value + } + } + fn verify_to_type_test( &mut self, generic_kind: GenericKind<'tcx>, @@ -150,7 +160,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { verify_bound: VerifyBound<'tcx>, ) -> TypeTest<'tcx> { let lower_bound = self.to_region_vid(region); - TypeTest { generic_kind, lower_bound, locations: self.locations, verify_bound } } @@ -162,10 +171,19 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } - fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) { + fn add_outlives( + &mut self, + sup: ty::RegionVid, + sub: ty::RegionVid, + category: ConstraintCategory<'tcx>, + ) { + let category = match self.category { + ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation => category, + _ => self.category, + }; self.constraints.outlives_constraints.push(OutlivesConstraint { locations: self.locations, - category: self.category, + category, span: self.span, sub, sup, @@ -185,10 +203,11 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<' _origin: SubregionOrigin<'tcx>, a: ty::Region<'tcx>, b: ty::Region<'tcx>, + constraint_category: ConstraintCategory<'tcx>, ) { let b = self.to_region_vid(b); let a = self.to_region_vid(a); - self.add_outlives(b, a); + self.add_outlives(b, a, constraint_category); } fn push_verify( @@ -198,6 +217,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<' a: ty::Region<'tcx>, bound: VerifyBound<'tcx>, ) { + let kind = self.replace_placeholders_with_nll(kind); + let bound = self.replace_placeholders_with_nll(bound); let type_test = self.verify_to_type_test(kind, a, bound); self.add_type_test(type_test); } 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 cc0318ede..029095926 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -1,5 +1,5 @@ use rustc_data_structures::frozen::Frozen; -use rustc_data_structures::transitive_relation::TransitiveRelation; +use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives; use rustc_infer::infer::outlives::env::RegionBoundPairs; @@ -8,7 +8,6 @@ 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_span::DUMMY_SP; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use type_op::TypeOpOutput; @@ -48,7 +47,7 @@ pub(crate) struct CreateResult<'tcx> { } pub(crate) fn create<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, implicit_region_bound: ty::Region<'tcx>, universal_regions: &Rc<UniversalRegions<'tcx>>, @@ -61,25 +60,13 @@ pub(crate) fn create<'tcx>( constraints, universal_regions: universal_regions.clone(), region_bound_pairs: Default::default(), - relations: UniversalRegionRelations { - universal_regions: universal_regions.clone(), - outlives: Default::default(), - inverse_outlives: Default::default(), - }, + outlives: Default::default(), + inverse_outlives: Default::default(), } .create() } impl UniversalRegionRelations<'_> { - /// Records in the `outlives_relation` (and - /// `inverse_outlives_relation`) that `fr_a: fr_b`. Invoked by the - /// builder below. - fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) { - debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b); - self.outlives.add(fr_a, fr_b); - self.inverse_outlives.add(fr_b, fr_a); - } - /// Given two universal regions, returns the postdominating /// upper-bound (effectively the least upper bound). /// @@ -209,19 +196,29 @@ impl UniversalRegionRelations<'_> { } struct UniversalRegionRelationsBuilder<'this, 'tcx> { - infcx: &'this InferCtxt<'this, 'tcx>, + infcx: &'this InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, universal_regions: Rc<UniversalRegions<'tcx>>, implicit_region_bound: ty::Region<'tcx>, constraints: &'this mut MirTypeckRegionConstraints<'tcx>, // outputs: - relations: UniversalRegionRelations<'tcx>, + outlives: TransitiveRelationBuilder<RegionVid>, + inverse_outlives: TransitiveRelationBuilder<RegionVid>, region_bound_pairs: RegionBoundPairs<'tcx>, } impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { + /// Records in the `outlives_relation` (and + /// `inverse_outlives_relation`) that `fr_a: fr_b`. + fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) { + debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b); + self.outlives.add(fr_a, fr_b); + self.inverse_outlives.add(fr_b, fr_a); + } + pub(crate) fn create(mut self) -> CreateResult<'tcx> { + let span = self.infcx.tcx.def_span(self.universal_regions.defining_ty.def_id()); let unnormalized_input_output_tys = self .universal_regions .unnormalized_input_tys @@ -242,10 +239,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { let constraint_sets: Vec<_> = unnormalized_input_output_tys .flat_map(|ty| { debug!("build: input_or_output={:?}", ty); - // We only add implied bounds for the normalized type as the unnormalized - // type may not actually get checked by the caller. - // - // Can otherwise be unsound, see #91068. + // We add implied bounds from both the unnormalized and normalized ty. + // See issue #87748 + let constraints_implied1 = self.add_implied_bounds(ty); let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self .param_env .and(type_op::normalize::Normalize::new(ty)) @@ -254,7 +250,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.infcx .tcx .sess - .delay_span_bug(DUMMY_SP, &format!("failed to normalize {:?}", ty)); + .delay_span_bug(span, &format!("failed to normalize {:?}", ty)); TypeOpOutput { output: self.infcx.tcx.ty_error(), constraints: None, @@ -269,13 +265,14 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // } // impl Foo for () { // type Bar = (); - // fn foo(&self) ->&() {} + // fn foo(&self) -> &() {} // } // ``` // Both &Self::Bar and &() are WF - let constraints_implied = self.add_implied_bounds(norm_ty); + let constraints_implied2 = + if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None }; normalized_inputs_and_output.push(norm_ty); - constraints1.into_iter().chain(constraints_implied) + constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2) }) .collect(); @@ -292,9 +289,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { let fr_fn_body = self.universal_regions.fr_fn_body; for fr in self.universal_regions.universal_regions() { debug!("build: relating free region {:?} to itself and to 'static", fr); - self.relations.relate_universal_regions(fr, fr); - self.relations.relate_universal_regions(fr_static, fr); - self.relations.relate_universal_regions(fr, fr_fn_body); + self.relate_universal_regions(fr, fr); + self.relate_universal_regions(fr_static, fr); + self.relate_universal_regions(fr, fr_fn_body); } for data in &constraint_sets { @@ -304,8 +301,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { &self.region_bound_pairs, self.implicit_region_bound, self.param_env, - Locations::All(DUMMY_SP), - DUMMY_SP, + Locations::All(span), + span, ConstraintCategory::Internal, &mut self.constraints, ) @@ -313,7 +310,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } CreateResult { - universal_region_relations: Frozen::freeze(self.relations), + universal_region_relations: Frozen::freeze(UniversalRegionRelations { + universal_regions: self.universal_regions, + outlives: self.outlives.freeze(), + inverse_outlives: self.inverse_outlives.freeze(), + }), region_bound_pairs: self.region_bound_pairs, normalized_inputs_and_output, } @@ -346,17 +347,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { match outlives_bound { OutlivesBound::RegionSubRegion(r1, r2) => { - // `where Type:` is lowered to `where Type: 'empty` so that - // we check `Type` is well formed, but there's no use for - // this bound here. - if r1.is_empty() { - return; - } - // The bound says that `r1 <= r2`; we store `r2: r1`. let r1 = self.universal_regions.to_region_vid(r1); let r2 = self.universal_regions.to_region_vid(r2); - self.relations.relate_universal_regions(r2, r1); + self.relate_universal_regions(r2, r1); } OutlivesBound::RegionSubParam(r_a, param_b) => { @@ -368,6 +362,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.region_bound_pairs .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); } + + OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => { + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a)); + } } } } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 4431a2e8e..a66ddd27d 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -7,16 +7,11 @@ //! `RETURN_PLACE` the MIR arguments) are always fully normalized (and //! contain revealed `impl Trait` values). -use crate::type_check::constraint_conversion::ConstraintConversion; use rustc_index::vec::Idx; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::mir::*; use rustc_middle::ty::Ty; use rustc_span::Span; -use rustc_span::DUMMY_SP; -use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; -use rustc_trait_selection::traits::query::Fallible; -use type_op::TypeOpOutput; use crate::universal_regions::UniversalRegions; @@ -185,7 +180,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - #[instrument(skip(self, span), level = "debug")] + #[instrument(skip(self), level = "debug")] fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) { if let Err(_) = self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation) @@ -194,13 +189,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // `rustc_traits::normalize_after_erasing_regions`. Ideally, we'd // like to normalize *before* inserting into `local_decls`, but // doing so ends up causing some other trouble. - let b = match self.normalize_and_add_constraints(b) { - Ok(n) => n, - Err(_) => { - debug!("equate_inputs_and_outputs: NoSolution"); - b - } - }; + let b = self.normalize(b, Locations::All(span)); // Note: if we have to introduce new placeholders during normalization above, then we won't have // added those universes to the universe info, which we would want in `relate_tys`. @@ -218,28 +207,4 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } } - - pub(crate) fn normalize_and_add_constraints(&mut self, t: Ty<'tcx>) -> Fallible<Ty<'tcx>> { - let TypeOpOutput { output: norm_ty, constraints, .. } = - self.param_env.and(type_op::normalize::Normalize::new(t)).fully_perform(self.infcx)?; - - debug!("{:?} normalized to {:?}", t, norm_ty); - - for data in constraints { - ConstraintConversion::new( - self.infcx, - &self.borrowck_context.universal_regions, - &self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - Locations::All(DUMMY_SP), - DUMMY_SP, - ConstraintCategory::Internal, - &mut self.borrowck_context.constraints, - ) - .convert_all(&*data); - } - - Ok(norm_ty) - } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d32b1edcd..3c1c3ab45 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -30,8 +30,9 @@ use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef, UserSubsts}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ - self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, OpaqueHiddenType, - OpaqueTypeKey, RegionVid, ToPredicate, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, + self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, Dynamic, + OpaqueHiddenType, OpaqueTypeKey, RegionVid, ToPredicate, Ty, TyCtxt, UserType, + UserTypeAnnotationIndex, }; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::{Span, DUMMY_SP}; @@ -122,7 +123,7 @@ mod relate_tys; /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis /// - `elements` -- MIR region map pub(crate) fn type_check<'mir, 'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, promoted: &IndexVec<Promoted, Body<'tcx>>, @@ -137,8 +138,6 @@ pub(crate) fn type_check<'mir, 'tcx>( use_polonius: bool, ) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); - let mut universe_causes = FxHashMap::default(); - universe_causes.insert(ty::UniverseIndex::from_u32(0), UniverseInfo::other()); let mut constraints = MirTypeckRegionConstraints { placeholder_indices: PlaceholderIndices::default(), placeholder_index_to_region: IndexVec::default(), @@ -147,7 +146,7 @@ pub(crate) fn type_check<'mir, 'tcx>( member_constraints: MemberConstraintSet::default(), closure_bounds_mapping: Default::default(), type_tests: Vec::default(), - universe_causes, + universe_causes: FxHashMap::default(), }; let CreateResult { @@ -164,9 +163,8 @@ pub(crate) fn type_check<'mir, 'tcx>( debug!(?normalized_inputs_and_output); - for u in ty::UniverseIndex::ROOT..infcx.universe() { - let info = UniverseInfo::other(); - constraints.universe_causes.insert(u, info); + for u in ty::UniverseIndex::ROOT..=infcx.universe() { + constraints.universe_causes.insert(u, UniverseInfo::other()); } let mut borrowck_context = BorrowCheckContext { @@ -178,97 +176,15 @@ pub(crate) fn type_check<'mir, 'tcx>( upvars, }; - let opaque_type_values = type_check_internal( + let mut checker = TypeChecker::new( infcx, - param_env, body, - promoted, + param_env, ®ion_bound_pairs, implicit_region_bound, &mut borrowck_context, - |mut cx| { - debug!("inside extra closure of type_check_internal"); - cx.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); - liveness::generate( - &mut cx, - body, - elements, - flow_inits, - move_data, - location_table, - use_polonius, - ); - - translate_outlives_facts(&mut cx); - let opaque_type_values = - infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - - opaque_type_values - .into_iter() - .map(|(opaque_type_key, decl)| { - cx.fully_perform_op( - Locations::All(body.span), - ConstraintCategory::OpaqueType, - CustomTypeOp::new( - |infcx| { - infcx.register_member_constraints( - param_env, - opaque_type_key, - decl.hidden_type.ty, - decl.hidden_type.span, - ); - Ok(InferOk { value: (), obligations: vec![] }) - }, - || "opaque_type_map".to_string(), - ), - ) - .unwrap(); - 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_infer_types_or_consts() { - infcx.tcx.sess.delay_span_bug( - decl.hidden_type.span, - &format!("could not resolve {:#?}", hidden_type.ty.kind()), - ); - hidden_type.ty = infcx.tcx.ty_error(); - } - - (opaque_type_key, (hidden_type, decl.origin)) - }) - .collect() - }, ); - MirTypeckResults { constraints, universal_region_relations, opaque_type_values } -} - -#[instrument( - skip(infcx, body, promoted, region_bound_pairs, borrowck_context, extra), - level = "debug" -)] -fn type_check_internal<'a, 'tcx, R>( - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body: &'a Body<'tcx>, - promoted: &'a IndexVec<Promoted, Body<'tcx>>, - region_bound_pairs: &'a RegionBoundPairs<'tcx>, - implicit_region_bound: ty::Region<'tcx>, - borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, - extra: impl FnOnce(TypeChecker<'a, 'tcx>) -> R, -) -> R { - debug!("body: {:#?}", body); - let mut checker = TypeChecker::new( - infcx, - body, - param_env, - region_bound_pairs, - implicit_region_bound, - borrowck_context, - ); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, promoted); verifier.visit_body(&body); @@ -280,7 +196,56 @@ fn type_check_internal<'a, 'tcx, R>( checker.typeck_mir(body); } - extra(checker) + checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); + liveness::generate( + &mut checker, + body, + elements, + flow_inits, + move_data, + location_table, + use_polonius, + ); + + translate_outlives_facts(&mut checker); + let opaque_type_values = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + + let opaque_type_values = opaque_type_values + .into_iter() + .map(|(opaque_type_key, decl)| { + checker + .fully_perform_op( + Locations::All(body.span), + ConstraintCategory::OpaqueType, + CustomTypeOp::new( + |infcx| { + infcx.register_member_constraints( + param_env, + opaque_type_key, + decl.hidden_type.ty, + decl.hidden_type.span, + ); + Ok(InferOk { value: (), obligations: vec![] }) + }, + || "opaque_type_map".to_string(), + ), + ) + .unwrap(); + 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() { + infcx.tcx.sess.delay_span_bug( + decl.hidden_type.span, + &format!("could not resolve {:#?}", hidden_type.ty.kind()), + ); + hidden_type.ty = infcx.tcx.ty_error(); + } + + (opaque_type_key, (hidden_type, decl.origin)) + }) + .collect(); + + MirTypeckResults { constraints, universal_region_relations, opaque_type_values } } fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { @@ -344,6 +309,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { + debug!(?constant, ?location, "visit_constant"); + self.super_constant(constant, location); let ty = self.sanitize_type(constant, constant.literal.ty()); @@ -387,11 +354,15 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { let tcx = self.tcx(); let maybe_uneval = match constant.literal { ConstantKind::Ty(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(uv) => Some(uv), + ty::ConstKind::Unevaluated(_) => { + bug!("should not encounter unevaluated ConstantKind::Ty here, got {:?}", ct) + } _ => None, }, + ConstantKind::Unevaluated(uv, _) => Some(uv), _ => None, }; + if let Some(uv) = maybe_uneval { if let Some(promoted) = uv.promoted { let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, @@ -454,12 +425,18 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() { + // const_trait_impl: use a non-const param env when checking that a FnDef type is well formed. + // this is because the well-formedness of the function does not need to be proved to have `const` + // impls for trait bounds. let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); + let prev = self.cx.param_env; + self.cx.param_env = prev.without_const(); self.cx.normalize_and_prove_instantiated_predicates( def_id, instantiated_predicates, locations, ); + self.cx.param_env = prev; } } } @@ -607,6 +584,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // modify their locations. let all_facts = &mut None; let mut constraints = Default::default(); + let mut type_tests = Default::default(); let mut closure_bounds = Default::default(); let mut liveness_constraints = LivenessValues::new(Rc::new(RegionValueElements::new(&promoted_body))); @@ -618,6 +596,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { &mut this.cx.borrowck_context.constraints.outlives_constraints, &mut constraints, ); + mem::swap(&mut this.cx.borrowck_context.constraints.type_tests, &mut type_tests); mem::swap( &mut this.cx.borrowck_context.constraints.closure_bounds_mapping, &mut closure_bounds, @@ -642,6 +621,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { swap_constraints(self); let locations = location.to_locations(); + + // Use location of promoted const in collected constraints + for type_test in type_tests.iter() { + let mut type_test = type_test.clone(); + type_test.locations = locations; + self.cx.borrowck_context.constraints.type_tests.push(type_test) + } for constraint in constraints.outlives().iter() { let mut constraint = constraint.clone(); constraint.locations = locations; @@ -790,6 +776,19 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } PlaceTy::from_ty(fty) } + ProjectionElem::OpaqueCast(ty) => { + let ty = self.sanitize_type(place, ty); + let ty = self.cx.normalize(ty, location); + self.cx + .eq_types( + base.ty, + ty, + location.to_locations(), + ConstraintCategory::TypeAnnotation, + ) + .unwrap(); + PlaceTy::from_ty(ty) + } } } @@ -883,7 +882,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { /// way, it accrues region constraints -- these can later be used by /// NLL region checking. struct TypeChecker<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, last_span: Span, body: &'a Body<'tcx>, @@ -953,7 +952,7 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { impl<'tcx> MirTypeckRegionConstraints<'tcx> { fn placeholder_region( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, placeholder: ty::PlaceholderRegion, ) -> ty::Region<'tcx> { let placeholder_index = self.placeholder_indices.insert(placeholder); @@ -1037,7 +1036,7 @@ impl Locations { impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn new( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, body: &'a Body<'tcx>, param_env: ty::ParamEnv<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, @@ -1076,6 +1075,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; let inferred_ty = self.normalize(inferred_ty, Locations::All(span)); let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty); + debug!(?annotation); match annotation { UserType::Ty(mut ty) => { ty = self.normalize(ty, Locations::All(span)); @@ -1195,10 +1195,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { tcx, self.param_env, proj, - |this, field, ()| { + |this, field, _| { let ty = this.field_ty(tcx, field); self.normalize(ty, locations) }, + |_, _| unreachable!(), ); curr_projected_ty = projected_ty; } @@ -1334,12 +1335,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } } - StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { - .. - }) => span_bug!( - stmt.source_info.span, - "Unexpected StatementKind::CopyNonOverlapping, should only appear after lowering_intrinsics", - ), + StatementKind::Intrinsic(box ref kind) => match kind { + NonDivergingIntrinsic::Assume(op) => self.check_operand(op, location), + NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!( + stmt.source_info.span, + "Unexpected NonDivergingIntrinsic::CopyNonOverlapping, should only appear after lowering_intrinsics", + ), + }, StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) @@ -1448,9 +1450,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { )) }); debug!(?sig); - let sig = self.normalize(sig, term_location); - self.check_call_dest(body, term, &sig, *destination, target, term_location); - + // IMPORTANT: We have to prove well formed for the function signature before + // we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc` + // get normalized away, causing us to ignore the `'b: 'a` bound used by the function. + // + // Normalization results in a well formed type if the input is well formed, so we + // don't have to check it twice. + // + // See #91068 for an example. self.prove_predicates( sig.inputs_and_output .iter() @@ -1458,6 +1465,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { term_location.to_locations(), ConstraintCategory::Boring, ); + let sig = self.normalize(sig, term_location); + self.check_call_dest(body, term, &sig, *destination, target, term_location); // The ordinary liveness rules will ensure that all // regions in the type of the callee are live here. We @@ -1621,7 +1630,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let op_arg_ty = self.normalize(op_arg_ty, term_location); let category = if from_hir_call { - ConstraintCategory::CallArgument(func_ty) + ConstraintCategory::CallArgument(self.infcx.tcx.erase_regions(func_ty)) } else { ConstraintCategory::Boring }; @@ -1774,7 +1783,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // `Sized` bound in no way depends on precise regions, so this // shouldn't affect `is_sized`. let erased_ty = tcx.erase_regions(ty); - if !erased_ty.is_sized(tcx.at(span), self.param_env) { + if !erased_ty.is_sized(tcx, self.param_env) { // in current MIR construction, all non-control-flow rvalue // expressions evaluate through `as_temp` or `into` a return // slot or local, so to find all unsized rvalues it is enough @@ -1834,14 +1843,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) { + debug!(?op, ?location, "check_operand"); + if let Operand::Constant(constant) = op { let maybe_uneval = match constant.literal { - ConstantKind::Ty(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(uv) => Some(uv), - _ => None, - }, - _ => None, + ConstantKind::Val(..) | ConstantKind::Ty(_) => None, + ConstantKind::Unevaluated(uv, _) => Some(uv), }; + if let Some(uv) = maybe_uneval { if uv.promoted.is_none() { let tcx = self.tcx(); @@ -1904,7 +1913,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - &Rvalue::NullaryOp(_, ty) => { + &Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => { let trait_ref = ty::TraitRef { def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), substs: tcx.mk_substs_trait(ty, &[]), @@ -2033,6 +2042,36 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } + CastKind::DynStar => { + // get the constraints from the target type (`dyn* Clone`) + // + // apply them to prove that the source type `Foo` implements `Clone` etc + let (existential_predicates, region) = match ty.kind() { + Dynamic(predicates, region, ty::DynStar) => (predicates, region), + _ => panic!("Invalid dyn* cast_ty"), + }; + + let self_ty = op.ty(body, tcx); + + self.prove_predicates( + existential_predicates + .iter() + .map(|predicate| predicate.with_self_ty(tcx, self_ty)), + location.to_locations(), + ConstraintCategory::Cast, + ); + + let outlives_predicate = + tcx.mk_predicate(Binder::dummy(ty::PredicateKind::TypeOutlives( + ty::OutlivesPredicate(self_ty, *region), + ))); + self.prove_predicate( + outlives_predicate, + location.to_locations(), + ConstraintCategory::Cast, + ); + } + CastKind::Pointer(PointerCast::MutToConstPointer) => { let ty::RawPtr(ty::TypeAndMut { ty: ty_from, @@ -2176,25 +2215,104 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } } - - CastKind::Misc => { + CastKind::IntToInt => { let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); - // Misc casts are either between floats and ints, or one ptr type to another. match (cast_ty_from, cast_ty_to) { - ( - Some(CastTy::Int(_) | CastTy::Float), - Some(CastTy::Int(_) | CastTy::Float), - ) - | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Ptr(_))) => (), + (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => (), _ => { span_mirbug!( self, rvalue, - "Invalid Misc cast {:?} -> {:?}", + "Invalid IntToInt cast {:?} -> {:?}", ty_from, - ty, + ty + ) + } + } + } + CastKind::IntToFloat => { + let ty_from = op.ty(body, tcx); + let cast_ty_from = CastTy::from_ty(ty_from); + let cast_ty_to = CastTy::from_ty(*ty); + match (cast_ty_from, cast_ty_to) { + (Some(CastTy::Int(_)), Some(CastTy::Float)) => (), + _ => { + span_mirbug!( + self, + rvalue, + "Invalid IntToFloat cast {:?} -> {:?}", + ty_from, + ty + ) + } + } + } + CastKind::FloatToInt => { + let ty_from = op.ty(body, tcx); + let cast_ty_from = CastTy::from_ty(ty_from); + let cast_ty_to = CastTy::from_ty(*ty); + match (cast_ty_from, cast_ty_to) { + (Some(CastTy::Float), Some(CastTy::Int(_))) => (), + _ => { + span_mirbug!( + self, + rvalue, + "Invalid FloatToInt cast {:?} -> {:?}", + ty_from, + ty + ) + } + } + } + CastKind::FloatToFloat => { + let ty_from = op.ty(body, tcx); + let cast_ty_from = CastTy::from_ty(ty_from); + let cast_ty_to = CastTy::from_ty(*ty); + match (cast_ty_from, cast_ty_to) { + (Some(CastTy::Float), Some(CastTy::Float)) => (), + _ => { + span_mirbug!( + self, + rvalue, + "Invalid FloatToFloat cast {:?} -> {:?}", + ty_from, + ty + ) + } + } + } + CastKind::FnPtrToPtr => { + let ty_from = op.ty(body, tcx); + let cast_ty_from = CastTy::from_ty(ty_from); + let cast_ty_to = CastTy::from_ty(*ty); + match (cast_ty_from, cast_ty_to) { + (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => (), + _ => { + span_mirbug!( + self, + rvalue, + "Invalid FnPtrToPtr cast {:?} -> {:?}", + ty_from, + ty + ) + } + } + } + CastKind::PtrToPtr => { + let ty_from = op.ty(body, tcx); + let cast_ty_from = CastTy::from_ty(ty_from); + let cast_ty_to = CastTy::from_ty(*ty); + match (cast_ty_from, cast_ty_to) { + (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => (), + _ => { + span_mirbug!( + self, + rvalue, + "Invalid PtrToPtr cast {:?} -> {:?}", + ty_from, + ty ) } } @@ -2490,6 +2608,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } ProjectionElem::Field(..) | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { @@ -2584,7 +2703,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .enumerate() .filter_map(|(idx, constraint)| { let ty::OutlivesPredicate(k1, r2) = - constraint.no_bound_vars().unwrap_or_else(|| { + constraint.0.no_bound_vars().unwrap_or_else(|| { bug!("query_constraint {:?} contained bound vars", constraint,); }); @@ -2659,7 +2778,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_local(&body, local, local_decl); } - for (block, block_data) in body.basic_blocks().iter_enumerated() { + for (block, block_data) in body.basic_blocks.iter_enumerated() { let mut location = Location { block, statement_index: 0 }; for stmt in &block_data.statements { if !stmt.source_info.span.is_dummy() { @@ -2710,7 +2829,7 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { /// constraints in our `InferCtxt` type ErrorInfo = InstantiateOpaqueType<'tcx>; - fn fully_perform(mut self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { + fn fully_perform(mut self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { let (mut output, region_constraints) = scrape_region_constraints(infcx, || { Ok(InferOk { value: (), obligations: self.obligations.clone() }) })?; diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index c97a6a1a6..4f2dc263b 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,6 +1,6 @@ use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; use rustc_infer::infer::NllRegionVariableOrigin; -use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::PredicateObligations; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::TypeRelation; @@ -155,27 +155,16 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> true } - fn register_opaque_type( + fn register_opaque_type_obligations( &mut self, - a: Ty<'tcx>, - b: Ty<'tcx>, - a_is_expected: bool, + obligations: PredicateObligations<'tcx>, ) -> Result<(), TypeError<'tcx>> { - let param_env = self.param_env(); - let span = self.span(); - let def_id = self.type_checker.body.source.def_id().expect_local(); - let body_id = self.type_checker.tcx().hir().local_def_id_to_hir_id(def_id); - let cause = ObligationCause::misc(span, body_id); self.type_checker .fully_perform_op( self.locations, self.category, InstantiateOpaqueType { - obligations: self - .type_checker - .infcx - .handle_opaque_type(a, b, a_is_expected, &cause, param_env)? - .obligations, + obligations, // These fields are filled in during execution of the operation base_universe: None, region_constraints: None, diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 2a7713bc4..2beb5e0ab 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -22,8 +22,8 @@ use rustc_hir::{BodyOwnerKind, HirId}; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use std::iter; use crate::nll::ToRegionVid; @@ -54,13 +54,6 @@ pub struct UniversalRegions<'tcx> { /// The total number of universal region variables instantiated. num_universals: usize, - /// A special region variable created for the `'empty(U0)` region. - /// Note that this is **not** a "universal" region, as it doesn't - /// represent a universally bound placeholder or any such thing. - /// But we do create it here in this type because it's a useful region - /// to have around in a few limited cases. - pub root_empty: RegionVid, - /// The "defining" type for this function, with all universal /// regions instantiated. For a closure or generator, this is the /// closure type, but for a top-level function it's the `FnDef`. @@ -226,7 +219,7 @@ impl<'tcx> UniversalRegions<'tcx> { /// signature. This will also compute the relationships that are /// known between those regions. pub fn new( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, mir_def: ty::WithOptConstParam<LocalDefId>, param_env: ty::ParamEnv<'tcx>, ) -> Self { @@ -323,11 +316,7 @@ impl<'tcx> UniversalRegions<'tcx> { /// See `UniversalRegionIndices::to_region_vid`. pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::ReEmpty(ty::UniverseIndex::ROOT) = *r { - self.root_empty - } else { - self.indices.to_region_vid(r) - } + self.indices.to_region_vid(r) } /// As part of the NLL unit tests, you can annotate a function with @@ -393,7 +382,7 @@ impl<'tcx> UniversalRegions<'tcx> { } struct UniversalRegionsBuilder<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, mir_def: ty::WithOptConstParam<LocalDefId>, mir_hir_id: HirId, param_env: ty::ParamEnv<'tcx>, @@ -425,7 +414,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.did.to_def_id()); - // If this is is a 'root' body (not a closure/generator/inline const), then + // If this is a 'root' body (not a closure/generator/inline const), then // there are no extern regions, so the local regions start at the same // position as the (empty) sub-list of extern regions let first_local_index = if self.mir_def.did.to_def_id() == typeck_root_def_id { @@ -501,16 +490,10 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { _ => None, }; - let root_empty = self - .infcx - .next_nll_region_var(NllRegionVariableOrigin::Existential { from_forall: true }) - .to_region_vid(); - UniversalRegions { indices, fr_static, fr_fn_body, - root_empty, first_extern_index, first_local_index, num_universals, @@ -716,7 +699,7 @@ trait InferCtxtExt<'tcx> { ); } -impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { fn replace_free_regions_with_nll_infer_vars<T>( &self, origin: NllRegionVariableOrigin, @@ -768,10 +751,9 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { mir_def_id: LocalDefId, indices: &mut UniversalRegionIndices<'tcx>, ) { - debug!("replace_late_bound_regions_with_nll_infer_vars(mir_def_id={:?})", mir_def_id); let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id()); for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| { - debug!("replace_late_bound_regions_with_nll_infer_vars: r={:?}", r); + debug!(?r); if !indices.indices.contains_key(&r) { let region_vid = self.next_nll_region_var(FR); debug!(?region_vid); |