diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:06:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:06:31 +0000 |
commit | 2ff14448863ac1a1dd9533461708e29aae170c2d (patch) | |
tree | 85b9fea2bbfe3f06473cfa381eed11f273b57c5c /compiler/rustc_borrowck | |
parent | Adding debian version 1.64.0+dfsg1-1. (diff) | |
download | rustc-2ff14448863ac1a1dd9533461708e29aae170c2d.tar.xz rustc-2ff14448863ac1a1dd9533461708e29aae170c2d.zip |
Adding debian version 1.65.0+dfsg1-2.debian/1.65.0+dfsg1-2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_borrowck')
27 files changed, 831 insertions, 569 deletions
diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs index 5e9cec5c3..144fd15fc 100644 --- a/compiler/rustc_borrowck/src/constraint_generation.rs +++ b/compiler/rustc_borrowck/src/constraint_generation.rs @@ -31,7 +31,7 @@ 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); } } 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/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..b1def1892 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -484,9 +484,7 @@ fn try_extract_error_from_region_constraints<'tcx>( }; nice_error.try_report_from_nll().or_else(|| { if let SubregionOrigin::Subtype(trace) = cause { - Some( - infcx.report_and_explain_type_error(*trace, &TypeError::RegionsPlaceholderMismatch), - ) + Some(infcx.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..f2204c242 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -258,7 +258,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( @@ -451,7 +452,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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 +527,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()); @@ -1124,6 +1120,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 +1128,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 +1184,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 +1457,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 +1467,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 { diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 72aee0267..1c01e78ab 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -15,7 +15,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 +38,7 @@ pub(crate) enum BorrowExplanation<'tcx> { span: Span, region_name: RegionName, opt_place_desc: Option<String>, + extra_info: Vec<ExtraConstraintInfo>, }, Unexplained, } @@ -243,6 +244,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 +270,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 +319,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,26 +345,22 @@ 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); + debug!(?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!(?region_sub); match find_use::find(body, regioncx, tcx, region_sub, location) { Some(Cause::LiveVar(local, location)) => { @@ -392,7 +401,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,19 +411,14 @@ 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 } } @@ -455,7 +459,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return outmost_back_edge; } - let block = &self.body.basic_blocks()[location.block]; + let block = &self.body.basic_blocks[location.block]; if location.statement_index < block.statements.len() { let successor = location.successor_within_block(); @@ -514,7 +518,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } if loop_head.dominates(from, &self.dominators) { - let block = &self.body.basic_blocks()[from.block]; + let block = &self.body.basic_blocks[from.block]; if from.statement_index < block.statements.len() { let successor = from.successor_within_block(); @@ -564,7 +568,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..683084cf0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1086,14 +1086,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 +1094,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..8d4c38d3a 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"), ); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 0ad4abbce..6b5014fa9 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 { @@ -309,7 +309,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 +364,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 +614,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 +628,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 +908,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { [ Expr { kind: - MethodCall( - path_segment, - _args, - span, - ), + MethodCall(path_segment, _, _, span), hir_id, .. }, @@ -810,10 +928,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) + .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 +970,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 +1022,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; } } @@ -928,7 +1048,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 +1072,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..34be2874f 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}, @@ -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, @@ -350,10 +354,11 @@ 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); @@ -462,6 +467,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 +501,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 +508,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 +540,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 +566,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 +685,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 +783,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 +860,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 +901,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..6c1eaa809 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -265,7 +265,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 +357,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 +369,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 +658,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 +678,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(); @@ -772,7 +768,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 +810,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, 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..86da87d06 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -3,7 +3,7 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] #![feature(let_chains)] -#![feature(let_else)] +#![cfg_attr(bootstrap, feature(let_else))] #![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -19,15 +19,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 +51,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; @@ -425,17 +427,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(); @@ -597,22 +591,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 +613,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 +976,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 +985,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 +1448,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: &[] }; 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..12b2481cc 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -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..6e5a96bee 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; } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 2894c6d29..244e6e342 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -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 @@ -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() { @@ -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 } @@ -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,15 +1385,13 @@ 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 @@ -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, @@ -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, @@ -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,9 +2228,9 @@ 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> { @@ -2338,7 +2312,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 +2328,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 +2345,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..9d088642f 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -16,6 +16,8 @@ use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::TraitEngineExt as _; +use crate::session_diagnostics::ConstNotUsedTraitAlias; + use super::RegionInferenceContext; impl<'tcx> RegionInferenceContext<'tcx> { @@ -58,7 +60,7 @@ 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>, @@ -107,7 +109,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, }); @@ -431,7 +433,7 @@ struct ReverseMapper<'tcx> { key: ty::OpaqueTypeKey<'tcx>, map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, - map_missing_regions_to_empty: bool, + do_not_error: bool, /// initially `Some`, set to `None` once error has been reported hidden_ty: Option<Ty<'tcx>>, @@ -448,29 +450,19 @@ impl<'tcx> ReverseMapper<'tcx> { hidden_ty: Ty<'tcx>, span: Span, ) -> Self { - Self { - tcx, - key, - map, - map_missing_regions_to_empty: false, - hidden_ty: Some(hidden_ty), - span, - } + Self { tcx, key, map, do_not_error: 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; + fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { + assert!(!self.do_not_error); + self.do_not_error = true; let kind = kind.fold_with(self); - self.map_missing_regions_to_empty = false; + self.do_not_error = false; kind } fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { - assert!(!self.map_missing_regions_to_empty); + assert!(!self.do_not_error); kind.fold_with(self) } } @@ -494,9 +486,9 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { ty::ReErased => return r, // The regions that we expect from borrow checking. - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {} + ty::ReEarlyBound(_) | ty::ReFree(_) => {} - ty::ReEmpty(_) | ty::RePlaceholder(_) | ty::ReVar(_) => { + 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. @@ -508,7 +500,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { 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 self.do_not_error => self.tcx.lifetimes.re_static, None if generics.parent.is_some() => { if let Some(hidden_ty) = self.hidden_ty.take() { unexpected_hidden_region_diagnostic( @@ -520,7 +512,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { ) .emit(); } - self.tcx.lifetimes.re_root_empty + self.tcx.lifetimes.re_static } None => { self.tcx @@ -572,7 +564,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { 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) + self.fold_kind_no_missing_regions_error(kind) } else { // ...but not elsewhere. self.fold_kind_normally(kind) @@ -587,7 +579,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { 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) + self.fold_kind_no_missing_regions_error(kind) } else { // ...but not elsewhere. self.fold_kind_normally(kind) @@ -639,17 +631,10 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { 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.sess.emit_err(ConstNotUsedTraitAlias { + ct: ct.to_string(), + span: self.span, + }); self.tcx().const_error(ct.ty()) } 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..63b2088f7 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -2,6 +2,7 @@ use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::mir::visit::{MutVisitor, TyContext}; use rustc_middle::mir::{Body, Location, Promoted}; +use rustc_middle::mir::{Constant, ConstantKind}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; @@ -37,6 +38,21 @@ where }) } +// FIXME(valtrees): This function is necessary because `fold_regions` +// panics for mir constants in the visitor. +// +// Once `visit_mir_constant` is removed we can also remove this function +// and just use `renumber_regions`. +fn renumber_regions_in_mir_constant<'tcx>( + infcx: &InferCtxt<'_, 'tcx>, + value: ConstantKind<'tcx>, +) -> ConstantKind<'tcx> { + infcx.tcx.super_fold_regions(value, |_region, _depth| { + let origin = NllRegionVariableOrigin::Existential { from_forall: false }; + infcx.next_nll_region_var(origin) + }) +} + struct NllVisitor<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, } @@ -48,6 +64,13 @@ impl<'a, 'tcx> NllVisitor<'a, 'tcx> { { renumber_regions(self.infcx, value) } + + fn renumber_regions_in_mir_constant( + &mut self, + value: ConstantKind<'tcx>, + ) -> ConstantKind<'tcx> { + renumber_regions_in_mir_constant(self.infcx, value) + } } impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> { @@ -77,7 +100,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_in_mir_constant(literal); + debug!("constant: {:#?}", constant); } } diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 895723d44..5d750c6ca 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::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::Span; +use crate::diagnostics::RegionName; + #[derive(SessionDiagnostic)] -#[error(borrowck::move_unsized, code = "E0161")] +#[diag(borrowck::move_unsized, code = "E0161")] pub(crate) struct MoveUnsized<'tcx> { pub ty: Ty<'tcx>, #[primary_span] @@ -12,7 +15,7 @@ pub(crate) struct MoveUnsized<'tcx> { } #[derive(SessionDiagnostic)] -#[error(borrowck::higher_ranked_lifetime_error)] +#[diag(borrowck::higher_ranked_lifetime_error)] pub(crate) struct HigherRankedLifetimeError { #[subdiagnostic] pub cause: Option<HigherRankedErrorCause>, @@ -29,16 +32,128 @@ pub(crate) enum HigherRankedErrorCause { } #[derive(SessionDiagnostic)] -#[error(borrowck::higher_ranked_subtype_error)] +#[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)] +#[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(SessionDiagnostic)] +#[diag(borrowck::const_not_used_in_type_alias)] +pub(crate) struct ConstNotUsedTraitAlias { + pub ct: String, + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[diag(borrowck::var_cannot_escape_closure)] +#[note] +#[note(borrowck::cannot_escape)] +pub(crate) struct FnMutError { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub ty_err: FnMutReturnTypeErr, +} + +#[derive(SessionSubdiagnostic)] +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(SessionSubdiagnostic)] +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(SessionDiagnostic)] +#[diag(borrowck::lifetime_constraints_error)] +pub(crate) struct LifetimeOutliveErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionSubdiagnostic)] +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(SessionSubdiagnostic)] +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..9271a2f4d 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); } @@ -90,17 +92,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 +119,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 +157,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..71eae0583 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}; @@ -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..f1b1c33a1 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; @@ -61,25 +61,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). /// @@ -216,11 +204,20 @@ struct UniversalRegionRelationsBuilder<'this, '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 unnormalized_input_output_tys = self .universal_regions @@ -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)) @@ -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 { @@ -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) => { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d32b1edcd..3713ec3f5 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}; @@ -178,97 +179,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 +199,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_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 } } fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { @@ -344,6 +312,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 +357,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>, @@ -1076,6 +1050,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)); @@ -1334,12 +1309,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 +1424,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 +1439,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 @@ -1834,14 +1817,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 +1887,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 +2016,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, @@ -2584,7 +2597,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 +2672,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() { diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 2a7713bc4..8cf9ed53d 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -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`. @@ -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 @@ -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, @@ -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); |