From 66db972281b513c30cdc8cfac4cd92b79f10ec30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 19 Feb 2024 17:11:13 +0000 Subject: [PATCH 01/19] Report a specialized error when a `'static` obligation comes from an `impl dyn Trait` ```text error: lifetime may not live long enough --> $DIR/static-impl-obligation.rs:8:27 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; | ^ cast requires that `'a` must outlive `'static` LL | y.hello(); | --------- calling this method introduces a `'static` lifetime requirement | help: relax the implicit `'static` bound on the impl | LL | impl dyn Foo + '_ { | ++++ ``` ```text error: lifetime may not live long enough --> $DIR/static-impl-obligation.rs:173:27 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; | ^ cast requires that `'a` must outlive `'static` LL | y.hello(); | --------- calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:169:20 | LL | impl dyn Foo + 'static where Self: 'static { | ^^^^^^^ ^^^^^^^ LL | fn hello(&self) where Self: 'static {} | ^^^^^^^ ``` --- Cargo.lock | 1 + compiler/rustc_borrowck/Cargo.toml | 1 + .../src/diagnostics/region_errors.rs | 306 +++++++++++++--- .../rustc_borrowck/src/region_infer/mod.rs | 2 + compiler/rustc_middle/src/traits/mod.rs | 3 + .../src/traits/error_reporting/suggestions.rs | 1 + tests/ui/lifetimes/static-impl-obligation.rs | 210 +++++++++++ .../lifetimes/static-impl-obligation.stderr | 336 ++++++++++++++++++ tests/ui/regions/issue-78262.base.stderr | 6 + tests/ui/regions/issue-78262.polonius.stderr | 6 + ...yn-trait-with-implicit-static-bound.stderr | 17 +- 11 files changed, 834 insertions(+), 55 deletions(-) create mode 100644 tests/ui/lifetimes/static-impl-obligation.rs create mode 100644 tests/ui/lifetimes/static-impl-obligation.stderr diff --git a/Cargo.lock b/Cargo.lock index 3110f32ade968..28a4e8eea098d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3580,6 +3580,7 @@ dependencies = [ "either", "itertools 0.12.1", "polonius-engine", + "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index bafc62c7318b4..a53267502f901 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" either = "1.5.0" itertools = "0.12" polonius-engine = "0.13.0" +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index c06bf94a6fd5f..ee703fdbdca9c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,5 +1,6 @@ //! Error reporting machinery for lifetime errors. +use rustc_ast::TraitObjectSyntax::Dyn; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; @@ -16,14 +17,12 @@ use rustc_infer::infer::{ HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, }, error_reporting::unexpected_hidden_region_diagnostic, - NllRegionVariableOrigin, RelateParamBound, + BoundRegionConversionTime, NllRegionVariableOrigin, RelateParamBound, }; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; -use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::TypeVisitor; -use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_middle::ty::{Region, TyCtxt}; +use rustc_middle::traits::ObligationCauseCode; +use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; @@ -499,19 +498,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } }; + self.explain_impl_static_obligation(&mut diag, cause.code(), outlived_fr); + match variance_info { ty::VarianceDiagInfo::None => {} ty::VarianceDiagInfo::Invariant { ty, param_index } => { let (desc, note) = match ty.kind() { ty::RawPtr(ty_mut) => { - assert_eq!(ty_mut.mutbl, rustc_hir::Mutability::Mut); + assert_eq!(ty_mut.mutbl, hir::Mutability::Mut); ( format!("a mutable pointer to `{}`", ty_mut.ty), "mutable pointers are invariant over their type parameter".to_string(), ) } ty::Ref(_, inner_ty, mutbl) => { - assert_eq!(*mutbl, rustc_hir::Mutability::Mut); + assert_eq!(*mutbl, hir::Mutability::Mut); ( format!("a mutable reference to `{inner_ty}`"), "mutable references are invariant over their type parameter" @@ -527,10 +528,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let adt_desc = adt.descr(); let desc = format!( - "the type `{ty}`, which makes the generic argument `{generic_arg}` invariant" + "the type `{ty}`, which makes the generic argument `{generic_arg}` \ + invariant" ); let note = format!( - "the {adt_desc} `{base_ty}` is invariant over the parameter `{base_generic_arg}`" + "the {adt_desc} `{base_ty}` is invariant over the parameter \ + `{base_generic_arg}`" ); (desc, note) } @@ -548,14 +551,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; diag.note(format!("requirement occurs because of {desc}",)); diag.note(note); - diag.help("see for more information about variance"); + diag.help( + "see for more \ + information about variance", + ); } } for extra in extra_info { match extra { ExtraConstraintInfo::PlaceholderFromPredicate(span) => { - diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); + diag.span_note( + span, + "due to current limitations in the borrow checker, this implies a \ + `'static` lifetime", + ); } } } @@ -563,6 +573,212 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.buffer_error(diag); } + /// Report a specialized error when a `'static` obligation comes from an `impl dyn Trait` + /// + /// ```text + /// error: lifetime may not live long enough + /// --> $DIR/static-impl-obligation.rs:8:27 + /// | + /// LL | fn bar<'a>(x: &'a &'a u32) { + /// | -- lifetime `'a` defined here + /// LL | let y: &dyn Foo = x; + /// | ^ cast requires that `'a` must outlive `'static` + /// LL | y.hello(); + /// | --------- calling this method introduces a `'static` lifetime requirement + /// | + /// note: the `impl` on `(dyn a::Foo + 'static)` has a `'static` lifetime requirement + /// --> $DIR/static-impl-obligation.rs:4:10 + /// | + /// LL | impl dyn Foo { + /// | ^^^^^^^ + /// help: relax the implicit `'static` bound on the impl + /// | + /// LL | impl dyn Foo + '_ { + /// | ++++ + /// ``` + /// ```text + /// error: lifetime may not live long enough + /// --> $DIR/static-impl-obligation.rs:173:27 + /// | + /// LL | fn bar<'a>(x: &'a &'a u32) { + /// | -- lifetime `'a` defined here + /// LL | let y: &dyn Foo = x; + /// | ^ cast requires that `'a` must outlive `'static` + /// LL | y.hello(); + /// | --------- calling this method introduces a `'static` lifetime requirement + /// | + /// note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements + /// --> $DIR/static-impl-obligation.rs:169:20 + /// | + /// LL | impl dyn Foo + 'static where Self: 'static { + /// | ^^^^^^^ ^^^^^^^ + /// LL | fn hello(&self) where Self: 'static {} + /// | ^^^^^^^ + /// ``` + fn explain_impl_static_obligation( + &self, + diag: &mut Diag<'_>, + code: &ObligationCauseCode<'tcx>, + outlived_fr: RegionVid, + ) { + let tcx = self.infcx.tcx; + let ObligationCauseCode::MethodCallConstraint(ty, call_span) = code else { + return; + }; + let ty::FnDef(def_id, args) = ty.kind() else { + return; + }; + let Ok(Some(instance)) = ty::Instance::resolve( + tcx, + self.param_env, + *def_id, + self.infcx.resolve_vars_if_possible(args), + ) else { + return; + }; + let def_id = instance.def_id(); + let parent = tcx.parent(def_id); + let hir::def::DefKind::Impl { .. } = tcx.def_kind(parent) else { + return; + }; + let ty = tcx.type_of(parent).instantiate(tcx, instance.args); + if self.to_error_region(outlived_fr) != Some(tcx.lifetimes.re_static) { + return; + } + // FIXME: there's a case that's yet to be handled: `impl dyn Trait + '_ where Self: '_` + // causes *two* errors to be produded, one about `where Self: '_` not being allowed, + // and the regular error with no additional information about "lifetime may not live + // long enough for `'static`" without mentioning where it came from. This is because + // our error recovery fallback is indeed `ReStatic`. We should at some point introduce + // a `ReError` instead to avoid this and other similar issues. + + // Look for `'static` bounds in the generics of the method and the `impl`. + // ``` + // impl dyn Trait where Self: 'static { + // fn foo(&self) where Self: 'static {} + // } + // ``` + let mut predicates: Vec = tcx + .predicates_of(def_id) + .predicates + .iter() + .chain(tcx.predicates_of(parent).predicates.iter()) + .filter_map(|(pred, pred_span)| { + if let Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r))) = + pred.kind().no_bound_vars() + // Look for `'static` bounds + && r.kind() == ty::ReStatic + // We only want bounds on `Self` + && self.infcx.can_eq(self.param_env, ty, pred_ty) + { + Some(*pred_span) + } else { + None + } + }) + .collect(); + + // Look at the receiver for `&'static self`, which introduces a `'static` obligation. + // ``` + // impl dyn Trait { + // fn foo(&'static self) {} + // } + // ``` + if let ty::Ref(region, _, _) = self + .infcx + .instantiate_binder_with_fresh_vars( + *call_span, + BoundRegionConversionTime::FnCall, + tcx.fn_sig(def_id).instantiate_identity().inputs().map_bound(|inputs| inputs[0]), + ) + .kind() + && *region == tcx.lifetimes.re_static + && let Some(assoc) = tcx.opt_associated_item(def_id) + && assoc.fn_has_self_parameter + { + // We have a `&'static self` receiver. + if let Some(def_id) = def_id.as_local() + && let owner = tcx.expect_hir_owner_node(def_id) + && let Some(decl) = owner.fn_decl() + && let Some(ty) = decl.inputs.get(0) + { + // Point at the `&'static self` receiver. + predicates.push(ty.span); + } else { + // The method is not defined on the local crate, point at the signature + // instead of just the receiver as an approximation. + predicates.push(tcx.def_span(def_id)) + } + } + + // When we have the HIR `Node` at hand, see if we can identify an + // implicit `'static` bound in an `impl dyn Trait {}` and if that's + // the only restriction, suggest relaxing it. + if let Some(hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Impl(hir::Impl { + self_ty: hir::Ty { kind: hir::TyKind::TraitObject(_, lt, _), span, .. }, + .. + }), + .. + })) = tcx.hir().get_if_local(parent) + && let Some(hir::Node::ImplItem(hir::ImplItem { .. })) = tcx.hir().get_if_local(def_id) + { + let suggestion = match lt.res { + hir::LifetimeName::ImplicitObjectLifetimeDefault if predicates.is_empty() => { + // `impl dyn Trait {}` + Some(( + span.shrink_to_hi(), + "consider relaxing the implicit `'static` requirement on the impl", + " + '_", + )) + } + hir::LifetimeName::Static if predicates.is_empty() => { + // `impl dyn Trait + 'static {}` + Some((lt.ident.span, "consider replacing this `'static` requirement", "'_")) + } + _ => None, + }; + if let Some((span, msg, sugg)) = suggestion { + // We only emit the suggestion to write `impl dyn Trait + '_ {}` if that's the only + // thing needed. + diag.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable); + // This is redundant but needed because we won't enter the section with the + // additional note, so we point at the method call here too. + diag.span_label( + *call_span, + "calling this method introduces a `'static` lifetime requirement", + ); + } else if let hir::LifetimeName::ImplicitObjectLifetimeDefault + | hir::LifetimeName::Static = lt.res + { + // Otherwise, we add the right span for the note pointing at all the places where + // a `'static` requirement is introduced when invoking the method on this `impl`. + predicates.push(lt.ident.span); + } + } else if let ty::Dynamic(_, region, ty::Dyn) = ty.kind() + && *region == tcx.lifetimes.re_static + { + // The `self_ty` has a `'static` bound on a `dyn Trait`, either implicit or explicit, + // but we don't have access to the HIR to identify which one nor to provide a targetted + // enough `Span`, so instead we fall back to pointing at the `impl` header instead. + predicates.push(tcx.def_span(parent)); + } + if !predicates.is_empty() { + diag.span_label( + *call_span, + "calling this method introduces a `'static` lifetime requirement", + ); + let a_static_lt = if predicates.len() == 1 { + "a `'static` lifetime requirement" + } else { + "`'static` lifetime requirements" + }; + let span: MultiSpan = predicates.into(); + diag.span_note(span, format!("the `impl` on `{ty}` has {a_static_lt}")); + } + } + /// Report a specialized error when `FnMut` closures return a reference to a captured variable. /// This function expects `fr` to be local and `outlived_fr` to not be local. /// @@ -810,7 +1026,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { 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 } @@ -927,37 +1142,32 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let tcx = self.infcx.tcx; - let instance = if let ConstraintCategory::CallArgument(Some(func_ty)) = category { - let (fn_did, args) = match func_ty.kind() { - ty::FnDef(fn_did, args) => (fn_did, args), - _ => return, - }; - debug!(?fn_did, ?args); + let ConstraintCategory::CallArgument(Some(func_ty)) = category else { + return; + }; + let ty::FnDef(fn_did, args) = func_ty.kind() else { + return; + }; + debug!(?fn_did, ?args); - // Only suggest this on function calls, not closures - let ty = tcx.type_of(fn_did).instantiate_identity(); - debug!("ty: {:?}, ty.kind: {:?}", ty, ty.kind()); - if let ty::Closure(_, _) = ty.kind() { - return; - } + // Only suggest this on function calls, not closures + let ty = tcx.type_of(fn_did).instantiate_identity(); + debug!("ty: {:?}, ty.kind: {:?}", ty, ty.kind()); + if let ty::Closure(_, _) = ty.kind() { + return; + } - if let Ok(Some(instance)) = ty::Instance::resolve( - tcx, - self.param_env, - *fn_did, - self.infcx.resolve_vars_if_possible(args), - ) { - instance - } else { - return; - } - } else { + let Ok(Some(instance)) = ty::Instance::resolve( + tcx, + self.param_env, + *fn_did, + self.infcx.resolve_vars_if_possible(args), + ) else { return; }; - let param = match find_param_with_region(tcx, f, o) { - Some(param) => param, - None => return, + let Some(param) = find_param_with_region(tcx, f, o) else { + return; }; debug!(?param); @@ -1001,12 +1211,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { "calling this method introduces the `impl`'s `'static` requirement", ); err.subdiagnostic(self.dcx(), RequireStaticErr::UsedImpl { multi_span }); - err.span_suggestion_verbose( - span.shrink_to_hi(), - "consider relaxing the implicit `'static` requirement", - " + '_", - Applicability::MaybeIncorrect, - ); + if let hir::TyKind::TraitObject(traits, lt, Dyn) = self_ty.kind + && lt.res == hir::LifetimeName::ImplicitObjectLifetimeDefault + && traits.iter().any(|t| t.span == *span) + { + // We already handle the case where `self_ty` has an implicit `'static` + // requirement specifically in `explain_impl_static_obligation`. + } else { + err.span_suggestion_verbose( + span.shrink_to_hi(), + "consider relaxing the implicit `'static` requirement", + " + '_", + Applicability::MaybeIncorrect, + ); + } suggested = true; } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index c3800a1f1f21b..2424968baace6 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -2051,6 +2051,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { CRATE_DEF_ID.to_def_id(), predicate_span, )) + } else if let ConstraintCategory::CallArgument(Some(fn_def)) = constraint.category { + Some(ObligationCauseCode::MethodCallConstraint(fn_def, constraint.span)) } else { None } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index a04bd636622ea..227dd666d583b 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -451,6 +451,9 @@ pub enum ObligationCauseCode<'tcx> { /// Obligations emitted during the normalization of a weak type alias. TypeAlias(InternedObligationCauseCode<'tcx>, Span, DefId), + + /// During borrowck we've found a method call that could have introduced a lifetime requirement. + MethodCallConstraint(Ty<'tcx>, Span), } /// Whether a value can be extracted into a const. diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index d19c2bd1f60ea..c25d37f502575 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2726,6 +2726,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::MethodReceiver | ObligationCauseCode::ReturnNoExpression | ObligationCauseCode::UnifyReceiver(..) + | ObligationCauseCode::MethodCallConstraint(..) | ObligationCauseCode::MiscObligation | ObligationCauseCode::WellFormed(..) | ObligationCauseCode::MatchImpl(..) diff --git a/tests/ui/lifetimes/static-impl-obligation.rs b/tests/ui/lifetimes/static-impl-obligation.rs new file mode 100644 index 0000000000000..011492eaee136 --- /dev/null +++ b/tests/ui/lifetimes/static-impl-obligation.rs @@ -0,0 +1,210 @@ +mod a { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo { //~ HELP consider relaxing the implicit `'static` requirement + fn hello(&self) {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod b { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo { + fn hello(&'static self) {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod c { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo { + fn hello(&'static self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod d { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo { + fn hello(&self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod e { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + 'static { //~ HELP consider replacing this `'static` requirement + fn hello(&self) {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod f { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + 'static { + fn hello(&'static self) {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod g { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + 'static { + fn hello(&'static self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod h { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + 'static { + fn hello(&self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod i { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo where Self: 'static { + fn hello(&self) {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod j { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo where Self: 'static { + fn hello(&'static self) {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod k { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo where Self: 'static { + fn hello(&'static self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod l { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo where Self: 'static { + fn hello(&self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod m { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + 'static where Self: 'static { + fn hello(&self) {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod n { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + 'static where Self: 'static { + fn hello(&'static self) {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod o { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + 'static where Self: 'static { + fn hello(&'static self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod p { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + 'static where Self: 'static { + fn hello(&self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +mod q { + struct Foo {} + impl Foo { + fn hello(&'static self) {} + } + fn bar<'a>(x: &'a &'a Foo) { + x.hello(); //~ ERROR borrowed data escapes outside of function + } +} +mod r { + struct Foo {} + impl Foo { + fn hello(&'static self) where Self: 'static {} + } + fn bar<'a>(x: &'a &'a Foo) { + x.hello(); //~ ERROR borrowed data escapes outside of function + } +} +mod s { + trait Foo {} + impl<'a> Foo for &'a u32 {} + + trait Trait { fn hello(&self) {} } + + impl Trait for dyn Foo { //~ HELP consider relaxing the implicit `'static` requirement on the impl + fn hello(&self) {} + + } + fn convert<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} +fn main() {} diff --git a/tests/ui/lifetimes/static-impl-obligation.stderr b/tests/ui/lifetimes/static-impl-obligation.stderr new file mode 100644 index 0000000000000..ccd005e34ede8 --- /dev/null +++ b/tests/ui/lifetimes/static-impl-obligation.stderr @@ -0,0 +1,336 @@ +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:8:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +help: consider relaxing the implicit `'static` requirement on the impl + | +LL | impl dyn Foo + '_ { + | ++++ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:19:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn b::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:15:10 + | +LL | impl dyn Foo { + | ^^^^^^^ +LL | fn hello(&'static self) {} + | ^^^^^^^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:30:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn c::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:26:10 + | +LL | impl dyn Foo { + | ^^^^^^^ +LL | fn hello(&'static self) where Self: 'static {} + | ^^^^^^^^^^^^^ ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:41:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn d::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:37:10 + | +LL | impl dyn Foo { + | ^^^^^^^ +LL | fn hello(&self) where Self: 'static {} + | ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:52:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +help: consider replacing this `'static` requirement + | +LL | impl dyn Foo + '_ { + | ~~ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:63:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn f::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:59:20 + | +LL | impl dyn Foo + 'static { + | ^^^^^^^ +LL | fn hello(&'static self) {} + | ^^^^^^^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:74:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn g::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:70:20 + | +LL | impl dyn Foo + 'static { + | ^^^^^^^ +LL | fn hello(&'static self) where Self: 'static {} + | ^^^^^^^^^^^^^ ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:85:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn h::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:81:20 + | +LL | impl dyn Foo + 'static { + | ^^^^^^^ +LL | fn hello(&self) where Self: 'static {} + | ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:96:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn i::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:92:10 + | +LL | impl dyn Foo where Self: 'static { + | ^^^^^^^ ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:107:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn j::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:103:10 + | +LL | impl dyn Foo where Self: 'static { + | ^^^^^^^ ^^^^^^^ +LL | fn hello(&'static self) {} + | ^^^^^^^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:118:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn k::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:114:10 + | +LL | impl dyn Foo where Self: 'static { + | ^^^^^^^ ^^^^^^^ +LL | fn hello(&'static self) where Self: 'static {} + | ^^^^^^^^^^^^^ ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:129:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn l::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:125:10 + | +LL | impl dyn Foo where Self: 'static { + | ^^^^^^^ ^^^^^^^ +LL | fn hello(&self) where Self: 'static {} + | ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:140:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn m::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:136:20 + | +LL | impl dyn Foo + 'static where Self: 'static { + | ^^^^^^^ ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:151:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn n::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:147:20 + | +LL | impl dyn Foo + 'static where Self: 'static { + | ^^^^^^^ ^^^^^^^ +LL | fn hello(&'static self) {} + | ^^^^^^^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:162:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn o::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:158:20 + | +LL | impl dyn Foo + 'static where Self: 'static { + | ^^^^^^^ ^^^^^^^ +LL | fn hello(&'static self) where Self: 'static {} + | ^^^^^^^^^^^^^ ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:173:27 + | +LL | fn bar<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:169:20 + | +LL | impl dyn Foo + 'static where Self: 'static { + | ^^^^^^^ ^^^^^^^ +LL | fn hello(&self) where Self: 'static {} + | ^^^^^^^ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/static-impl-obligation.rs:183:9 + | +LL | fn bar<'a>(x: &'a &'a Foo) { + | -- - `x` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +LL | x.hello(); + | ^^^^^^^^^ + | | + | `x` escapes the function body here + | argument requires that `'a` must outlive `'static` + | calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `q::Foo` has a `'static` lifetime requirement + --> $DIR/static-impl-obligation.rs:180:18 + | +LL | fn hello(&'static self) {} + | ^^^^^^^^^^^^^ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/static-impl-obligation.rs:192:9 + | +LL | fn bar<'a>(x: &'a &'a Foo) { + | -- - `x` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +LL | x.hello(); + | ^^^^^^^^^ + | | + | `x` escapes the function body here + | argument requires that `'a` must outlive `'static` + | calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `r::Foo` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:189:18 + | +LL | fn hello(&'static self) where Self: 'static {} + | ^^^^^^^^^^^^^ ^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:206:27 + | +LL | fn convert<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` +LL | y.hello(); + | --------- calling this method introduces a `'static` lifetime requirement + | +help: consider relaxing the implicit `'static` requirement on the impl + | +LL | impl Trait for dyn Foo + '_ { + | ++++ + +error: aborting due to 19 previous errors + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/regions/issue-78262.base.stderr b/tests/ui/regions/issue-78262.base.stderr index 113c84c656287..bf915ef35e9a3 100644 --- a/tests/ui/regions/issue-78262.base.stderr +++ b/tests/ui/regions/issue-78262.base.stderr @@ -6,8 +6,14 @@ LL | let f = |x: &dyn TT| x.func(); | | | | | | | `x` escapes the closure body here | | | argument requires that `'1` must outlive `'static` + | | | calling this method introduces a `'static` lifetime requirement | | let's call the lifetime of this reference `'1` | `x` is a reference that is only valid in the closure body + | +help: consider relaxing the implicit `'static` requirement on the impl + | +LL | impl dyn TT + '_ { + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/regions/issue-78262.polonius.stderr b/tests/ui/regions/issue-78262.polonius.stderr index 113c84c656287..bf915ef35e9a3 100644 --- a/tests/ui/regions/issue-78262.polonius.stderr +++ b/tests/ui/regions/issue-78262.polonius.stderr @@ -6,8 +6,14 @@ LL | let f = |x: &dyn TT| x.func(); | | | | | | | `x` escapes the closure body here | | | argument requires that `'1` must outlive `'static` + | | | calling this method introduces a `'static` lifetime requirement | | let's call the lifetime of this reference `'1` | `x` is a reference that is only valid in the closure body + | +help: consider relaxing the implicit `'static` requirement on the impl + | +LL | impl dyn TT + '_ { + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr index 6effaf61099ec..7b918d8ee01f6 100644 --- a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr +++ b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr @@ -10,6 +10,7 @@ LL | val.use_self::() | | | `val` escapes the function body here | argument requires that `'a` must outlive `'static` + | calling this method introduces a `'static` lifetime requirement | note: the used `impl` has a `'static` requirement --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:15:32 @@ -18,7 +19,7 @@ LL | impl MyTrait for dyn ObjectTrait { | ^^^^^^^^^^^^^^ this has an implicit `'static` lifetime requirement LL | fn use_self(&self) -> &() { panic!() } | -------- calling this method introduces the `impl`'s `'static` requirement -help: consider relaxing the implicit `'static` requirement +help: consider relaxing the implicit `'static` requirement on the impl | LL | impl MyTrait for dyn ObjectTrait + '_ { | ++++ @@ -35,6 +36,7 @@ LL | val.use_self() | | | `val` escapes the function body here | argument requires that `'a` must outlive `'static` + | calling this method introduces a `'static` lifetime requirement | note: the used `impl` has a `'static` requirement --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:67:14 @@ -43,7 +45,7 @@ LL | impl dyn ObjectTrait { | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement LL | fn use_self(&self) -> &() { panic!() } | -------- calling this method introduces the `impl`'s `'static` requirement -help: consider relaxing the implicit `'static` requirement +help: consider relaxing the implicit `'static` requirement on the impl | LL | impl dyn ObjectTrait + '_ { | ++++ @@ -69,10 +71,6 @@ LL | fn use_self(&self) -> &() { panic!() } ... LL | impl MyTrait for dyn ObjectTrait {} | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement -help: consider relaxing the implicit `'static` requirement - | -LL | impl MyTrait for dyn ObjectTrait + '_ {} - | ++++ error[E0521]: borrowed data escapes outside of function --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:112:9 @@ -95,10 +93,6 @@ LL | fn use_self(&self) -> &() { panic!() } ... LL | impl MyTrait for dyn ObjectTrait {} | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement -help: consider relaxing the implicit `'static` requirement - | -LL | impl MyTrait for dyn ObjectTrait + '_ {} - | ++++ error[E0521]: borrowed data escapes outside of function --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:38:9 @@ -112,6 +106,7 @@ LL | val.use_self() | | | `val` escapes the function body here | argument requires that `'a` must outlive `'static` + | calling this method introduces a `'static` lifetime requirement | note: the used `impl` has a `'static` requirement --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:32:26 @@ -120,7 +115,7 @@ LL | impl MyTrait for dyn ObjectTrait { | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement LL | fn use_self(&self) -> &() { panic!() } | -------- calling this method introduces the `impl`'s `'static` requirement -help: consider relaxing the implicit `'static` requirement +help: consider relaxing the implicit `'static` requirement on the impl | LL | impl MyTrait for dyn ObjectTrait + '_ { | ++++ From 8949695ca5d153e785ae21d4df2f5211e28c4e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 19 Feb 2024 18:41:16 +0000 Subject: [PATCH 02/19] Modify primary span on lifetime error --- .../src/diagnostics/region_errors.rs | 2 + .../rustc_borrowck/src/region_infer/mod.rs | 2 +- tests/ui/lifetimes/static-impl-obligation.rs | 68 ++++++------ .../lifetimes/static-impl-obligation.stderr | 102 +++++++++--------- 4 files changed, 88 insertions(+), 86 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index ee703fdbdca9c..17e11f504539d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -745,6 +745,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { diag.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable); // This is redundant but needed because we won't enter the section with the // additional note, so we point at the method call here too. + diag.replace_span_with(*call_span, false); diag.span_label( *call_span, "calling this method introduces a `'static` lifetime requirement", @@ -765,6 +766,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { predicates.push(tcx.def_span(parent)); } if !predicates.is_empty() { + diag.replace_span_with(*call_span, false); diag.span_label( *call_span, "calling this method introduces a `'static` lifetime requirement", diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 2424968baace6..c44f92f6cb534 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -2057,7 +2057,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { None } }) - .unwrap_or_else(|| ObligationCauseCode::MiscObligation); + .unwrap_or(ObligationCauseCode::MiscObligation); // Classify each of the constraints along the path. let mut categorized_path: Vec> = path diff --git a/tests/ui/lifetimes/static-impl-obligation.rs b/tests/ui/lifetimes/static-impl-obligation.rs index 011492eaee136..7e71dfab09fbe 100644 --- a/tests/ui/lifetimes/static-impl-obligation.rs +++ b/tests/ui/lifetimes/static-impl-obligation.rs @@ -5,8 +5,8 @@ mod a { fn hello(&self) {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod b { @@ -16,8 +16,8 @@ mod b { fn hello(&'static self) {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod c { @@ -27,8 +27,8 @@ mod c { fn hello(&'static self) where Self: 'static {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod d { @@ -38,8 +38,8 @@ mod d { fn hello(&self) where Self: 'static {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod e { @@ -49,8 +49,8 @@ mod e { fn hello(&self) {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod f { @@ -60,8 +60,8 @@ mod f { fn hello(&'static self) {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod g { @@ -71,8 +71,8 @@ mod g { fn hello(&'static self) where Self: 'static {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod h { @@ -82,8 +82,8 @@ mod h { fn hello(&self) where Self: 'static {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod i { @@ -93,8 +93,8 @@ mod i { fn hello(&self) {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod j { @@ -104,8 +104,8 @@ mod j { fn hello(&'static self) {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod k { @@ -115,8 +115,8 @@ mod k { fn hello(&'static self) where Self: 'static {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod l { @@ -126,8 +126,8 @@ mod l { fn hello(&self) where Self: 'static {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod m { @@ -137,8 +137,8 @@ mod m { fn hello(&self) {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod n { @@ -148,8 +148,8 @@ mod n { fn hello(&'static self) {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod o { @@ -159,8 +159,8 @@ mod o { fn hello(&'static self) where Self: 'static {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod p { @@ -170,8 +170,8 @@ mod p { fn hello(&self) where Self: 'static {} } fn bar<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } mod q { @@ -203,8 +203,8 @@ mod s { } fn convert<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } fn main() {} diff --git a/tests/ui/lifetimes/static-impl-obligation.stderr b/tests/ui/lifetimes/static-impl-obligation.stderr index ccd005e34ede8..4685d6b768c81 100644 --- a/tests/ui/lifetimes/static-impl-obligation.stderr +++ b/tests/ui/lifetimes/static-impl-obligation.stderr @@ -1,12 +1,12 @@ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:8:27 + --> $DIR/static-impl-obligation.rs:9:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | help: consider relaxing the implicit `'static` requirement on the impl | @@ -14,14 +14,14 @@ LL | impl dyn Foo + '_ { | ++++ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:19:27 + --> $DIR/static-impl-obligation.rs:20:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn b::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:15:10 @@ -32,14 +32,14 @@ LL | fn hello(&'static self) {} | ^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:30:27 + --> $DIR/static-impl-obligation.rs:31:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn c::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:26:10 @@ -50,14 +50,14 @@ LL | fn hello(&'static self) where Self: 'static {} | ^^^^^^^^^^^^^ ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:41:27 + --> $DIR/static-impl-obligation.rs:42:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn d::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:37:10 @@ -68,14 +68,14 @@ LL | fn hello(&self) where Self: 'static {} | ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:52:27 + --> $DIR/static-impl-obligation.rs:53:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | help: consider replacing this `'static` requirement | @@ -83,14 +83,14 @@ LL | impl dyn Foo + '_ { | ~~ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:63:27 + --> $DIR/static-impl-obligation.rs:64:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn f::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:59:20 @@ -101,14 +101,14 @@ LL | fn hello(&'static self) {} | ^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:74:27 + --> $DIR/static-impl-obligation.rs:75:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn g::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:70:20 @@ -119,14 +119,14 @@ LL | fn hello(&'static self) where Self: 'static {} | ^^^^^^^^^^^^^ ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:85:27 + --> $DIR/static-impl-obligation.rs:86:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn h::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:81:20 @@ -137,14 +137,14 @@ LL | fn hello(&self) where Self: 'static {} | ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:96:27 + --> $DIR/static-impl-obligation.rs:97:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn i::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:92:10 @@ -153,14 +153,14 @@ LL | impl dyn Foo where Self: 'static { | ^^^^^^^ ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:107:27 + --> $DIR/static-impl-obligation.rs:108:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn j::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:103:10 @@ -171,14 +171,14 @@ LL | fn hello(&'static self) {} | ^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:118:27 + --> $DIR/static-impl-obligation.rs:119:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn k::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:114:10 @@ -189,14 +189,14 @@ LL | fn hello(&'static self) where Self: 'static {} | ^^^^^^^^^^^^^ ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:129:27 + --> $DIR/static-impl-obligation.rs:130:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn l::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:125:10 @@ -207,14 +207,14 @@ LL | fn hello(&self) where Self: 'static {} | ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:140:27 + --> $DIR/static-impl-obligation.rs:141:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn m::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:136:20 @@ -223,14 +223,14 @@ LL | impl dyn Foo + 'static where Self: 'static { | ^^^^^^^ ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:151:27 + --> $DIR/static-impl-obligation.rs:152:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn n::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:147:20 @@ -241,14 +241,14 @@ LL | fn hello(&'static self) {} | ^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:162:27 + --> $DIR/static-impl-obligation.rs:163:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn o::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:158:20 @@ -259,14 +259,14 @@ LL | fn hello(&'static self) where Self: 'static {} | ^^^^^^^^^^^^^ ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:173:27 + --> $DIR/static-impl-obligation.rs:174:9 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:169:20 @@ -317,14 +317,14 @@ LL | fn hello(&'static self) where Self: 'static {} | ^^^^^^^^^^^^^ ^^^^^^^ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:206:27 + --> $DIR/static-impl-obligation.rs:207:9 | LL | fn convert<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` LL | y.hello(); - | --------- calling this method introduces a `'static` lifetime requirement + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | help: consider relaxing the implicit `'static` requirement on the impl | From afa5995e0249652fe376f1b378655a41595b72f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 19 Feb 2024 18:52:39 +0000 Subject: [PATCH 03/19] Tweak lifetime error --- .../src/diagnostics/region_errors.rs | 24 +++++++++---------- tests/ui/lifetimes/static-impl-obligation.rs | 2 +- .../lifetimes/static-impl-obligation.stderr | 4 +--- tests/ui/regions/issue-78262.base.stderr | 1 - tests/ui/regions/issue-78262.polonius.stderr | 1 - ...yn-trait-with-implicit-static-bound.stderr | 3 --- 6 files changed, 13 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 17e11f504539d..3ee4a3cd3f6a8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -711,6 +711,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } + let mut new_primary_span = false; // When we have the HIR `Node` at hand, see if we can identify an // implicit `'static` bound in an `impl dyn Trait {}` and if that's // the only restriction, suggest relaxing it. @@ -735,7 +736,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } hir::LifetimeName::Static if predicates.is_empty() => { // `impl dyn Trait + 'static {}` - Some((lt.ident.span, "consider replacing this `'static` requirement", "'_")) + Some((lt.ident.span, "consider relaxing this `'static` requirement", "'_")) } _ => None, }; @@ -743,13 +744,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // We only emit the suggestion to write `impl dyn Trait + '_ {}` if that's the only // thing needed. diag.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable); - // This is redundant but needed because we won't enter the section with the - // additional note, so we point at the method call here too. - diag.replace_span_with(*call_span, false); - diag.span_label( - *call_span, - "calling this method introduces a `'static` lifetime requirement", - ); + new_primary_span = true; } else if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static = lt.res { @@ -766,11 +761,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { predicates.push(tcx.def_span(parent)); } if !predicates.is_empty() { - diag.replace_span_with(*call_span, false); - diag.span_label( - *call_span, - "calling this method introduces a `'static` lifetime requirement", - ); + new_primary_span = true; let a_static_lt = if predicates.len() == 1 { "a `'static` lifetime requirement" } else { @@ -779,6 +770,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let span: MultiSpan = predicates.into(); diag.span_note(span, format!("the `impl` on `{ty}` has {a_static_lt}")); } + if new_primary_span && diag.span.primary_span() != Some(*call_span) { + diag.replace_span_with(*call_span, false); + diag.span_label( + *call_span, + "calling this method introduces a `'static` lifetime requirement", + ); + } } /// Report a specialized error when `FnMut` closures return a reference to a captured variable. diff --git a/tests/ui/lifetimes/static-impl-obligation.rs b/tests/ui/lifetimes/static-impl-obligation.rs index 7e71dfab09fbe..6208861b7d19a 100644 --- a/tests/ui/lifetimes/static-impl-obligation.rs +++ b/tests/ui/lifetimes/static-impl-obligation.rs @@ -45,7 +45,7 @@ mod d { mod e { trait Foo {} impl<'a> Foo for &'a u32 {} - impl dyn Foo + 'static { //~ HELP consider replacing this `'static` requirement + impl dyn Foo + 'static { //~ HELP consider relaxing this `'static` requirement fn hello(&self) {} } fn bar<'a>(x: &'a &'a u32) { diff --git a/tests/ui/lifetimes/static-impl-obligation.stderr b/tests/ui/lifetimes/static-impl-obligation.stderr index 4685d6b768c81..aa23f5f1997ee 100644 --- a/tests/ui/lifetimes/static-impl-obligation.stderr +++ b/tests/ui/lifetimes/static-impl-obligation.stderr @@ -77,7 +77,7 @@ LL | let y: &dyn Foo = x; LL | y.hello(); | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement | -help: consider replacing this `'static` requirement +help: consider relaxing this `'static` requirement | LL | impl dyn Foo + '_ { | ~~ @@ -288,7 +288,6 @@ LL | x.hello(); | | | `x` escapes the function body here | argument requires that `'a` must outlive `'static` - | calling this method introduces a `'static` lifetime requirement | note: the `impl` on `q::Foo` has a `'static` lifetime requirement --> $DIR/static-impl-obligation.rs:180:18 @@ -308,7 +307,6 @@ LL | x.hello(); | | | `x` escapes the function body here | argument requires that `'a` must outlive `'static` - | calling this method introduces a `'static` lifetime requirement | note: the `impl` on `r::Foo` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:189:18 diff --git a/tests/ui/regions/issue-78262.base.stderr b/tests/ui/regions/issue-78262.base.stderr index bf915ef35e9a3..f267c5280beee 100644 --- a/tests/ui/regions/issue-78262.base.stderr +++ b/tests/ui/regions/issue-78262.base.stderr @@ -6,7 +6,6 @@ LL | let f = |x: &dyn TT| x.func(); | | | | | | | `x` escapes the closure body here | | | argument requires that `'1` must outlive `'static` - | | | calling this method introduces a `'static` lifetime requirement | | let's call the lifetime of this reference `'1` | `x` is a reference that is only valid in the closure body | diff --git a/tests/ui/regions/issue-78262.polonius.stderr b/tests/ui/regions/issue-78262.polonius.stderr index bf915ef35e9a3..f267c5280beee 100644 --- a/tests/ui/regions/issue-78262.polonius.stderr +++ b/tests/ui/regions/issue-78262.polonius.stderr @@ -6,7 +6,6 @@ LL | let f = |x: &dyn TT| x.func(); | | | | | | | `x` escapes the closure body here | | | argument requires that `'1` must outlive `'static` - | | | calling this method introduces a `'static` lifetime requirement | | let's call the lifetime of this reference `'1` | `x` is a reference that is only valid in the closure body | diff --git a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr index 7b918d8ee01f6..2c7af8bfc8833 100644 --- a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr +++ b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr @@ -10,7 +10,6 @@ LL | val.use_self::() | | | `val` escapes the function body here | argument requires that `'a` must outlive `'static` - | calling this method introduces a `'static` lifetime requirement | note: the used `impl` has a `'static` requirement --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:15:32 @@ -36,7 +35,6 @@ LL | val.use_self() | | | `val` escapes the function body here | argument requires that `'a` must outlive `'static` - | calling this method introduces a `'static` lifetime requirement | note: the used `impl` has a `'static` requirement --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:67:14 @@ -106,7 +104,6 @@ LL | val.use_self() | | | `val` escapes the function body here | argument requires that `'a` must outlive `'static` - | calling this method introduces a `'static` lifetime requirement | note: the used `impl` has a `'static` requirement --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:32:26 From aebcb29a83ac75939048bf21f71e11d92ab7aded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 19 Feb 2024 19:09:37 +0000 Subject: [PATCH 04/19] Fix clippy tests --- src/tools/clippy/tests/ui/crashes/ice-6256.fixed | 15 +++++++++++++++ src/tools/clippy/tests/ui/crashes/ice-6256.stderr | 5 +++++ 2 files changed, 20 insertions(+) create mode 100644 src/tools/clippy/tests/ui/crashes/ice-6256.fixed diff --git a/src/tools/clippy/tests/ui/crashes/ice-6256.fixed b/src/tools/clippy/tests/ui/crashes/ice-6256.fixed new file mode 100644 index 0000000000000..84fb95c955728 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-6256.fixed @@ -0,0 +1,15 @@ +// originally from rustc ./tests/ui/regions/issue-78262.rs +// ICE: to get the signature of a closure, use args.as_closure().sig() not fn_sig() +#![allow(clippy::upper_case_acronyms)] + +trait TT {} + +impl dyn TT + '_ { + fn func(&self) {} +} + +#[rustfmt::skip] +fn main() { + let f = |x: &dyn TT| x.func(); + //~^ ERROR: borrowed data escapes outside of closure +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-6256.stderr b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr index 922772a6147bc..48d5bc2bbd54a 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6256.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr @@ -8,6 +8,11 @@ LL | let f = |x: &dyn TT| x.func(); | | | argument requires that `'1` must outlive `'static` | | let's call the lifetime of this reference `'1` | `x` is a reference that is only valid in the closure body + | +help: consider relaxing the implicit `'static` requirement on the impl + | +LL | impl dyn TT + '_ { + | ++++ error: aborting due to 1 previous error From cbf4781ac233d81c834b5cbc281cf2a6e34d7e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 19 Feb 2024 22:36:01 +0000 Subject: [PATCH 05/19] Tweak logic to account for methods implemented on trait --- .../src/diagnostics/region_errors.rs | 28 +++++++++++++++---- ...yn-trait-with-implicit-static-bound.stderr | 8 ++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3ee4a3cd3f6a8..3295d36d97a87 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -637,11 +637,28 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { return; }; let def_id = instance.def_id(); - let parent = tcx.parent(def_id); - let hir::def::DefKind::Impl { .. } = tcx.def_kind(parent) else { - return; - }; - let ty = tcx.type_of(parent).instantiate(tcx, instance.args); + let mut parent = tcx.parent(def_id); + match tcx.def_kind(parent) { + hir::def::DefKind::Impl { .. } => {} + hir::def::DefKind::Trait => { + let Some(ty) = args.get(0).and_then(|arg| arg.as_type()) else { + return; + }; + let mut impls = vec![]; + tcx.for_each_relevant_impl(parent, ty, |id| { + impls.push(id); + }); + if let [def_id] = impls[..] { + // The method we have is on the trait, but for `parent` we want to analyze the + // relevant impl instead. + parent = def_id; + } else { + return; + }; + } + _ => return, + } + let ty = tcx.type_of(parent).instantiate_identity(); if self.to_error_region(outlived_fr) != Some(tcx.lifetimes.re_static) { return; } @@ -723,7 +740,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }), .. })) = tcx.hir().get_if_local(parent) - && let Some(hir::Node::ImplItem(hir::ImplItem { .. })) = tcx.hir().get_if_local(def_id) { let suggestion = match lt.res { hir::LifetimeName::ImplicitObjectLifetimeDefault if predicates.is_empty() => { diff --git a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr index 2c7af8bfc8833..ebc5807e0415b 100644 --- a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr +++ b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr @@ -69,6 +69,10 @@ LL | fn use_self(&self) -> &() { panic!() } ... LL | impl MyTrait for dyn ObjectTrait {} | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +help: consider relaxing the implicit `'static` requirement on the impl + | +LL | impl MyTrait for dyn ObjectTrait + '_ {} + | ++++ error[E0521]: borrowed data escapes outside of function --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:112:9 @@ -91,6 +95,10 @@ LL | fn use_self(&self) -> &() { panic!() } ... LL | impl MyTrait for dyn ObjectTrait {} | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +help: consider relaxing the implicit `'static` requirement on the impl + | +LL | impl MyTrait for dyn ObjectTrait + '_ {} + | ++++ error[E0521]: borrowed data escapes outside of function --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:38:9 From b870786ef39e174dc094b7ded6b93a5f80a4bd57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 20 Feb 2024 20:55:02 +0000 Subject: [PATCH 06/19] On E0478, point at source of lifetime requirement --- .../src/infer/error_reporting/mod.rs | 51 ++++++++- tests/ui/lifetimes/static-impl-obligation.rs | 69 ++++++++++++ .../lifetimes/static-impl-obligation.stderr | 105 +++++++++++++++++- tests/ui/static/static-lifetime.stderr | 5 + 4 files changed, 224 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 331b3b97c3496..4ea4faa840602 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -61,7 +61,7 @@ use crate::traits::{ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, - ErrorGuaranteed, IntoDiagArg, + ErrorGuaranteed, IntoDiagArg, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -458,11 +458,54 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // the error. If all of these fails, we fall back to a rather // general bit of code that displays the error information RegionResolutionError::ConcreteFailure(origin, sub, sup) => { - if sub.is_placeholder() || sup.is_placeholder() { - self.report_placeholder_failure(origin, sub, sup).emit() + let mut err = if sub.is_placeholder() || sup.is_placeholder() { + self.report_placeholder_failure(origin, sub, sup) } else { - self.report_concrete_failure(origin, sub, sup).emit() + self.report_concrete_failure(origin, sub, sup) + }; + if let hir::def::DefKind::Impl { .. } = + self.tcx.def_kind(generic_param_scope) + && let Some(def_id) = + self.tcx.trait_id_of_impl(generic_param_scope.into()) + { + // Collect all the `Span`s corresponding to the predicates introducing + // the `sub` lifetime that couldn't be met (sometimes `'static`) on + // both the `trait` and the `impl`. + let spans: Vec = self + .tcx + .predicates_of(def_id) + .predicates + .iter() + .chain( + self.tcx.predicates_of(generic_param_scope).predicates.iter(), + ) + .filter_map(|(pred, span)| { + if let Some(ty::ClauseKind::TypeOutlives( + ty::OutlivesPredicate(pred_ty, r), + )) = pred.kind().no_bound_vars() + && r == sub + && let ty::Param(param) = pred_ty.kind() + && param.name.as_str() == "Self" + { + Some(*span) + } else { + None + } + }) + .collect(); + + if !spans.is_empty() { + let spans_len = spans.len(); + err.span_note( + MultiSpan::from(spans), + format!( + "`{sub}` requirement{} introduced here", + pluralize!(spans_len), + ), + ); + } } + err.emit() } RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self diff --git a/tests/ui/lifetimes/static-impl-obligation.rs b/tests/ui/lifetimes/static-impl-obligation.rs index 6208861b7d19a..585a6328ca8ca 100644 --- a/tests/ui/lifetimes/static-impl-obligation.rs +++ b/tests/ui/lifetimes/static-impl-obligation.rs @@ -207,4 +207,73 @@ mod s { y.hello(); //~ ERROR lifetime may not live long enough } } +mod t { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait where Self: 'static { + fn use_self(&self) -> &() where Self: 'static { panic!() } + } + trait Irrelevant { + fn use_self(&self) -> &() { panic!() } + } + + impl MyTrait for dyn ObjectTrait + '_ {} //~ ERROR lifetime bound not satisfied + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + val.use_self() //~ ERROR borrowed data escapes + } +} +mod u { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait { + fn use_self(&self) -> &() where Self: 'static { panic!() } + } + trait Irrelevant { + fn use_self(&self) -> &() { panic!() } + } + + impl MyTrait for dyn ObjectTrait + '_ {} + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + val.use_self() //~ ERROR borrowed data escapes + } +} +mod v { + trait OtherTrait<'a> {} + impl<'a> OtherTrait<'a> for &'a () {} + + trait ObjectTrait {} + trait MyTrait where Self: 'static { + fn use_self(&'static self) -> &() { panic!() } + } + trait Irrelevant { + fn use_self(&self) -> &() { panic!() } + } + + impl MyTrait for dyn ObjectTrait {} + + fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + val.use_self() //~ ERROR borrowed data escapes + } +} +mod w { + trait Foo {} + impl<'a> Foo for &'a u32 {} + + trait Trait where Self: 'static { fn hello(&self) {} } + + impl Trait for dyn Foo + '_ { //~ERROR lifetime bound not satisfied + fn hello(&self) {} + + } + fn convert<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} fn main() {} diff --git a/tests/ui/lifetimes/static-impl-obligation.stderr b/tests/ui/lifetimes/static-impl-obligation.stderr index aa23f5f1997ee..98b6e17ec5184 100644 --- a/tests/ui/lifetimes/static-impl-obligation.stderr +++ b/tests/ui/lifetimes/static-impl-obligation.stderr @@ -1,3 +1,95 @@ +error[E0478]: lifetime bound not satisfied + --> $DIR/static-impl-obligation.rs:222:10 + | +LL | impl MyTrait for dyn ObjectTrait + '_ {} + | ^^^^^^^ + | +note: lifetime parameter instantiated with the anonymous lifetime as defined here + --> $DIR/static-impl-obligation.rs:222:40 + | +LL | impl MyTrait for dyn ObjectTrait + '_ {} + | ^^ + = note: but lifetime parameter must outlive the static lifetime +note: `'static` requirement introduced here + --> $DIR/static-impl-obligation.rs:215:31 + | +LL | trait MyTrait where Self: 'static { + | ^^^^^^^ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/static-impl-obligation.rs:225:9 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | -- --- `val` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +LL | val.use_self() + | ^^^^^^^^^^^^^^ + | | + | `val` escapes the function body here + | argument requires that `'a` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/static-impl-obligation.rs:243:9 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | -- --- `val` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +LL | val.use_self() + | ^^^^^^^^^^^^^^ + | | + | `val` escapes the function body here + | argument requires that `'a` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/static-impl-obligation.rs:261:9 + | +LL | fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { + | -- --- `val` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +LL | val.use_self() + | ^^^^^^^^^^^^^^ + | | + | `val` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +note: the used `impl` has a `'static` requirement + --> $DIR/static-impl-obligation.rs:258:26 + | +LL | fn use_self(&'static self) -> &() { panic!() } + | -------- calling this method introduces the `impl`'s `'static` requirement +... +LL | impl MyTrait for dyn ObjectTrait {} + | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement +note: the `impl` on `(dyn v::ObjectTrait + 'static)` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:252:21 + | +LL | fn use_self(&'static self) -> &() { panic!() } + | ^^^^^^^^^^^^^ +... +LL | impl MyTrait for dyn ObjectTrait {} + | ^^^^^^^^^^^^^^^ + +error[E0478]: lifetime bound not satisfied + --> $DIR/static-impl-obligation.rs:270:10 + | +LL | impl Trait for dyn Foo + '_ { + | ^^^^^ + | +note: lifetime parameter instantiated with the anonymous lifetime as defined here + --> $DIR/static-impl-obligation.rs:270:30 + | +LL | impl Trait for dyn Foo + '_ { + | ^^ + = note: but lifetime parameter must outlive the static lifetime +note: `'static` requirement introduced here + --> $DIR/static-impl-obligation.rs:268:29 + | +LL | trait Trait where Self: 'static { fn hello(&self) {} } + | ^^^^^^^ + error: lifetime may not live long enough --> $DIR/static-impl-obligation.rs:9:9 | @@ -329,6 +421,15 @@ help: consider relaxing the implicit `'static` requirement on the impl LL | impl Trait for dyn Foo + '_ { | ++++ -error: aborting due to 19 previous errors +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:275:27 + | +LL | fn convert<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` + +error: aborting due to 25 previous errors -For more information about this error, try `rustc --explain E0521`. +Some errors have detailed explanations: E0478, E0521. +For more information about an error, try `rustc --explain E0478`. diff --git a/tests/ui/static/static-lifetime.stderr b/tests/ui/static/static-lifetime.stderr index 8c9434ce3cb0b..c6c775dc79582 100644 --- a/tests/ui/static/static-lifetime.stderr +++ b/tests/ui/static/static-lifetime.stderr @@ -10,6 +10,11 @@ note: lifetime parameter instantiated with the lifetime `'a` as defined here LL | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {} | ^^ = note: but lifetime parameter must outlive the static lifetime +note: `'static` requirement introduced here + --> $DIR/static-lifetime.rs:1:30 + | +LL | pub trait Arbitrary: Sized + 'static {} + | ^^^^^^^ error: aborting due to 1 previous error From edb3338a1410655516dca2290ee0485db70d6c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 20 Feb 2024 21:47:28 +0000 Subject: [PATCH 07/19] Elaborate predicates to also display transitive obligations --- .../src/diagnostics/region_errors.rs | 45 +++++++++++-------- .../lifetimes/static-impl-obligation.stderr | 12 +++++ 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3295d36d97a87..5a8c410bbdfd8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -25,6 +25,7 @@ use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; +use rustc_trait_selection::traits; use crate::borrowck_errors; use crate::session_diagnostics::{ @@ -675,25 +676,31 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // fn foo(&self) where Self: 'static {} // } // ``` - let mut predicates: Vec = tcx - .predicates_of(def_id) - .predicates - .iter() - .chain(tcx.predicates_of(parent).predicates.iter()) - .filter_map(|(pred, pred_span)| { - if let Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r))) = - pred.kind().no_bound_vars() - // Look for `'static` bounds - && r.kind() == ty::ReStatic - // We only want bounds on `Self` - && self.infcx.can_eq(self.param_env, ty, pred_ty) - { - Some(*pred_span) - } else { - None - } - }) - .collect(); + let mut predicates: Vec = traits::elaborate( + tcx, + tcx.predicates_of(def_id).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), + ) + .chain(traits::elaborate( + tcx, + tcx.predicates_of(parent).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), + )) + .filter_map(|(pred, pred_span)| { + if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() + && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r)) = clause + // Look for `'static` bounds + && r.kind() == ty::ReStatic + // We only want bounds on `Self` + && (self.infcx.can_eq(self.param_env, ty, pred_ty) + || matches!( + pred_ty.kind(), + ty::Param(name) if name.name.as_str() == "Self")) + { + Some(pred_span) + } else { + None + } + }) + .collect(); // Look at the receiver for `&'static self`, which introduces a `'static` obligation. // ``` diff --git a/tests/ui/lifetimes/static-impl-obligation.stderr b/tests/ui/lifetimes/static-impl-obligation.stderr index 98b6e17ec5184..bfc5ca83c6c4f 100644 --- a/tests/ui/lifetimes/static-impl-obligation.stderr +++ b/tests/ui/lifetimes/static-impl-obligation.stderr @@ -28,6 +28,12 @@ LL | val.use_self() | | | `val` escapes the function body here | argument requires that `'a` must outlive `'static` + | +note: the `impl` on `dyn t::ObjectTrait` has a `'static` lifetime requirement + --> $DIR/static-impl-obligation.rs:216:47 + | +LL | fn use_self(&self) -> &() where Self: 'static { panic!() } + | ^^^^^^^ error[E0521]: borrowed data escapes outside of function --> $DIR/static-impl-obligation.rs:243:9 @@ -41,6 +47,12 @@ LL | val.use_self() | | | `val` escapes the function body here | argument requires that `'a` must outlive `'static` + | +note: the `impl` on `dyn u::ObjectTrait` has a `'static` lifetime requirement + --> $DIR/static-impl-obligation.rs:234:47 + | +LL | fn use_self(&self) -> &() where Self: 'static { panic!() } + | ^^^^^^^ error[E0521]: borrowed data escapes outside of function --> $DIR/static-impl-obligation.rs:261:9 From d89dba1b6336d93f60c929ec1776aabb1a227807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 20 Feb 2024 22:00:00 +0000 Subject: [PATCH 08/19] Cleanup --- .../src/diagnostics/region_errors.rs | 17 +++++++++++------ .../src/infer/error_reporting/mod.rs | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 5a8c410bbdfd8..94728d0e35d6a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -678,12 +678,17 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // ``` let mut predicates: Vec = traits::elaborate( tcx, - tcx.predicates_of(def_id).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), + tcx.predicates_of(def_id) + .predicates + .iter() + .map(|(p, sp)| (p.as_predicate(), *sp)) + .chain( + tcx.predicates_of(parent) + .predicates + .iter() + .map(|(p, sp)| (p.as_predicate(), *sp)), + ), ) - .chain(traits::elaborate( - tcx, - tcx.predicates_of(parent).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), - )) .filter_map(|(pred, pred_span)| { if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r)) = clause @@ -693,7 +698,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && (self.infcx.can_eq(self.param_env, ty, pred_ty) || matches!( pred_ty.kind(), - ty::Param(name) if name.name.as_str() == "Self")) + ty::Param(name) if name.name == kw::SelfUpper)) { Some(pred_span) } else { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 4ea4faa840602..53156e6f74e16 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -485,7 +485,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { )) = pred.kind().no_bound_vars() && r == sub && let ty::Param(param) = pred_ty.kind() - && param.name.as_str() == "Self" + && param.name == kw::SelfUpper { Some(*span) } else { From 6fde87c73386faa25dd9aa5580d8f963c1cfd239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 20 Feb 2024 22:40:20 +0000 Subject: [PATCH 09/19] Surface yet more trait obligations --- .../src/diagnostics/region_errors.rs | 34 ++++++++++--------- .../lifetimes/static-impl-obligation.stderr | 10 ++++-- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 94728d0e35d6a..d565d7c81b385 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -623,6 +623,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { outlived_fr: RegionVid, ) { let tcx = self.infcx.tcx; + debug!(?code); let ObligationCauseCode::MethodCallConstraint(ty, call_span) = code else { return; }; @@ -637,10 +638,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ) else { return; }; + debug!(?instance); let def_id = instance.def_id(); let mut parent = tcx.parent(def_id); - match tcx.def_kind(parent) { - hir::def::DefKind::Impl { .. } => {} + debug!(?def_id, ?parent); + let trait_preds = match tcx.def_kind(parent) { + hir::def::DefKind::Impl { .. } => &[], hir::def::DefKind::Trait => { let Some(ty) = args.get(0).and_then(|arg| arg.as_type()) else { return; @@ -652,14 +655,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let [def_id] = impls[..] { // The method we have is on the trait, but for `parent` we want to analyze the // relevant impl instead. + let preds = tcx.predicates_of(parent).predicates; parent = def_id; + preds } else { return; - }; + } } _ => return, - } + }; + debug!(?def_id, ?parent); let ty = tcx.type_of(parent).instantiate_identity(); + debug!(?ty); if self.to_error_region(outlived_fr) != Some(tcx.lifetimes.re_static) { return; } @@ -678,23 +685,17 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // ``` let mut predicates: Vec = traits::elaborate( tcx, - tcx.predicates_of(def_id) - .predicates - .iter() - .map(|(p, sp)| (p.as_predicate(), *sp)) - .chain( - tcx.predicates_of(parent) - .predicates - .iter() - .map(|(p, sp)| (p.as_predicate(), *sp)), - ), + tcx.predicates_of(def_id).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), ) + .chain(traits::elaborate( + tcx, + tcx.predicates_of(parent).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), + )) + .chain(traits::elaborate(tcx, trait_preds.iter().map(|(p, sp)| (p.as_predicate(), *sp)))) .filter_map(|(pred, pred_span)| { if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r)) = clause - // Look for `'static` bounds && r.kind() == ty::ReStatic - // We only want bounds on `Self` && (self.infcx.can_eq(self.param_env, ty, pred_ty) || matches!( pred_ty.kind(), @@ -706,6 +707,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } }) .collect(); + debug!(?predicates); // Look at the receiver for `&'static self`, which introduces a `'static` obligation. // ``` diff --git a/tests/ui/lifetimes/static-impl-obligation.stderr b/tests/ui/lifetimes/static-impl-obligation.stderr index bfc5ca83c6c4f..a84956b08b3a6 100644 --- a/tests/ui/lifetimes/static-impl-obligation.stderr +++ b/tests/ui/lifetimes/static-impl-obligation.stderr @@ -29,9 +29,11 @@ LL | val.use_self() | `val` escapes the function body here | argument requires that `'a` must outlive `'static` | -note: the `impl` on `dyn t::ObjectTrait` has a `'static` lifetime requirement - --> $DIR/static-impl-obligation.rs:216:47 +note: the `impl` on `dyn t::ObjectTrait` has `'static` lifetime requirements + --> $DIR/static-impl-obligation.rs:215:31 | +LL | trait MyTrait where Self: 'static { + | ^^^^^^^ LL | fn use_self(&self) -> &() where Self: 'static { panic!() } | ^^^^^^^ @@ -76,8 +78,10 @@ LL | fn use_self(&'static self) -> &() { panic!() } LL | impl MyTrait for dyn ObjectTrait {} | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement note: the `impl` on `(dyn v::ObjectTrait + 'static)` has `'static` lifetime requirements - --> $DIR/static-impl-obligation.rs:252:21 + --> $DIR/static-impl-obligation.rs:251:31 | +LL | trait MyTrait where Self: 'static { + | ^^^^^^^ LL | fn use_self(&'static self) -> &() { panic!() } | ^^^^^^^^^^^^^ ... From 2d573b6cb33f439e9a45fe1cdb97b8445426ba14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 20 Feb 2024 22:50:14 +0000 Subject: [PATCH 10/19] Always look at the trait bounds (not just when looking at a trait `DefId`) --- .../rustc_borrowck/src/diagnostics/region_errors.rs | 4 +++- tests/ui/lifetimes/static-impl-obligation.rs | 4 ++-- tests/ui/lifetimes/static-impl-obligation.stderr | 12 ++++++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index d565d7c81b385..861e32d0861f3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -643,7 +643,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut parent = tcx.parent(def_id); debug!(?def_id, ?parent); let trait_preds = match tcx.def_kind(parent) { - hir::def::DefKind::Impl { .. } => &[], + hir::def::DefKind::Impl { .. } => { + tcx.trait_id_of_impl(parent).map_or(&[][..], |id| tcx.predicates_of(id).predicates) + } hir::def::DefKind::Trait => { let Some(ty) = args.get(0).and_then(|arg| arg.as_type()) else { return; diff --git a/tests/ui/lifetimes/static-impl-obligation.rs b/tests/ui/lifetimes/static-impl-obligation.rs index 585a6328ca8ca..69178a7375f63 100644 --- a/tests/ui/lifetimes/static-impl-obligation.rs +++ b/tests/ui/lifetimes/static-impl-obligation.rs @@ -272,8 +272,8 @@ mod w { } fn convert<'a>(x: &'a &'a u32) { - let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough - y.hello(); + let y: &dyn Foo = x; + y.hello(); //~ ERROR lifetime may not live long enough } } fn main() {} diff --git a/tests/ui/lifetimes/static-impl-obligation.stderr b/tests/ui/lifetimes/static-impl-obligation.stderr index a84956b08b3a6..0eba240eb51d4 100644 --- a/tests/ui/lifetimes/static-impl-obligation.stderr +++ b/tests/ui/lifetimes/static-impl-obligation.stderr @@ -438,12 +438,20 @@ LL | impl Trait for dyn Foo + '_ { | ++++ error: lifetime may not live long enough - --> $DIR/static-impl-obligation.rs:275:27 + --> $DIR/static-impl-obligation.rs:276:9 | LL | fn convert<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; - | ^ cast requires that `'a` must outlive `'static` + | - cast requires that `'a` must outlive `'static` +LL | y.hello(); + | ^^^^^^^^^ calling this method introduces a `'static` lifetime requirement + | +note: the `impl` on `dyn w::Foo` has a `'static` lifetime requirement + --> $DIR/static-impl-obligation.rs:268:29 + | +LL | trait Trait where Self: 'static { fn hello(&self) {} } + | ^^^^^^^ error: aborting due to 25 previous errors From 6d8c08a3b1099799b41b536752b744e8b573fa79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 21 Feb 2024 05:52:30 +0000 Subject: [PATCH 11/19] `#[allow(rustc::untranslatable_diagnostic)]` for now --- compiler/rustc_borrowck/src/diagnostics/region_errors.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 861e32d0861f3..131084f91f64a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -616,6 +616,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// LL | fn hello(&self) where Self: 'static {} /// | ^^^^^^^ /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] fn explain_impl_static_obligation( &self, diag: &mut Diag<'_>, From b27b9fd46f29dc1b158f32af16173c7b5da5b151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 21 Feb 2024 06:33:54 +0000 Subject: [PATCH 12/19] Account for more cases --- .../src/diagnostics/region_errors.rs | 21 +++++---- .../lifetimes/dyn-trait-static-obligation.rs | 47 +++++++++++++++++++ .../dyn-trait-static-obligation.stderr | 24 ++++++++++ 3 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 tests/ui/lifetimes/dyn-trait-static-obligation.rs create mode 100644 tests/ui/lifetimes/dyn-trait-static-obligation.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 131084f91f64a..6371858ebd18c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -499,7 +499,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } }; - self.explain_impl_static_obligation(&mut diag, cause.code(), outlived_fr); + if let ConstraintCategory::CallArgument(Some(ty)) = category { + self.explain_impl_static_obligation(&mut diag, ty, cause.span, outlived_fr); + } else if let ObligationCauseCode::MethodCallConstraint(ty, call_span) = cause.code() { + self.explain_impl_static_obligation(&mut diag, *ty, *call_span, outlived_fr); + } match variance_info { ty::VarianceDiagInfo::None => {} @@ -621,14 +625,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn explain_impl_static_obligation( &self, diag: &mut Diag<'_>, - code: &ObligationCauseCode<'tcx>, + ty: Ty<'tcx>, + call_span: Span, outlived_fr: RegionVid, ) { let tcx = self.infcx.tcx; - debug!(?code); - let ObligationCauseCode::MethodCallConstraint(ty, call_span) = code else { - return; - }; let ty::FnDef(def_id, args) = ty.kind() else { return; }; @@ -722,7 +723,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let ty::Ref(region, _, _) = self .infcx .instantiate_binder_with_fresh_vars( - *call_span, + call_span, BoundRegionConversionTime::FnCall, tcx.fn_sig(def_id).instantiate_identity().inputs().map_bound(|inputs| inputs[0]), ) @@ -804,10 +805,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let span: MultiSpan = predicates.into(); diag.span_note(span, format!("the `impl` on `{ty}` has {a_static_lt}")); } - if new_primary_span && diag.span.primary_span() != Some(*call_span) { - diag.replace_span_with(*call_span, false); + if new_primary_span && diag.span.primary_span() != Some(call_span) { + diag.replace_span_with(call_span, false); diag.span_label( - *call_span, + call_span, "calling this method introduces a `'static` lifetime requirement", ); } diff --git a/tests/ui/lifetimes/dyn-trait-static-obligation.rs b/tests/ui/lifetimes/dyn-trait-static-obligation.rs new file mode 100644 index 0000000000000..1c129c1728203 --- /dev/null +++ b/tests/ui/lifetimes/dyn-trait-static-obligation.rs @@ -0,0 +1,47 @@ +use std::cell::*; + +#[derive(Default)] +struct Test { + pub foo: u32, +} + +trait FooSetter { + fn set_foo(&mut self, value: u32); +} + +impl FooSetter for Test { + fn set_foo(&mut self, value: u32) { + self.foo = value; + } +} + +trait BaseSetter{ + fn set(&mut self, value: u32); +} +impl BaseSetter for dyn FooSetter { + fn set(&mut self, value: u32){ + self.set_foo(value); + } +} + +struct TestHolder<'a> { + pub holder: Option>, +} + +impl <'a>TestHolder<'a>{ + pub fn test_foo(&self){ + self.holder.as_ref().unwrap().borrow_mut().set(20); + //~^ ERROR borrowed data escapes outside of method + } +} + +fn main() { + let mut test = Test::default(); + test.foo = 10; + { + let holder = TestHolder { holder: Some(RefCell::from(&mut test))}; + + holder.test_foo(); + } + test.foo = 30; +} diff --git a/tests/ui/lifetimes/dyn-trait-static-obligation.stderr b/tests/ui/lifetimes/dyn-trait-static-obligation.stderr new file mode 100644 index 0000000000000..bc22e6ef2e9a3 --- /dev/null +++ b/tests/ui/lifetimes/dyn-trait-static-obligation.stderr @@ -0,0 +1,24 @@ +error[E0521]: borrowed data escapes outside of method + --> $DIR/dyn-trait-static-obligation.rs:33:8 + | +LL | impl <'a>TestHolder<'a>{ + | -- lifetime `'a` defined here +LL | pub fn test_foo(&self){ + | ----- `self` is a reference that is only valid in the method body +LL | self.holder.as_ref().unwrap().borrow_mut().set(20); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `self` escapes the method body here + | argument requires that `'a` must outlive `'static` + | + = note: requirement occurs because of a mutable reference to `dyn FooSetter` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance +help: consider relaxing the implicit `'static` requirement on the impl + | +LL | impl BaseSetter for dyn FooSetter + '_ { + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0521`. From 551e89eb19cb09d46e1ed5a80c3fe413610bb991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 21 Feb 2024 19:47:18 +0000 Subject: [PATCH 13/19] Provide more detail on lifetime failure of trait object in field type ``` error[E0478]: lifetime bound not satisfied --> tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:21 | 4 | broken: Box | ^^^ ^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18 | 3 | struct Something<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime note: unmet `'static` obligations introduced here --> rust/library/core/src/any.rs:115:16 | 115 | pub trait Any: 'static { | ^^^^^^^ ``` Fix #83325. --- .../src/infer/error_reporting/mod.rs | 172 +++++++++++++++--- tests/ui/error-codes/E0478.stderr | 4 +- ...e-obligation-from-trait-in-trait-object.rs | 6 + ...ligation-from-trait-in-trait-object.stderr | 18 ++ ...unds-on-objects-and-type-parameters.stderr | 4 +- .../ui/regions/regions-wf-trait-object.stderr | 4 +- 6 files changed, 179 insertions(+), 29 deletions(-) create mode 100644 tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs create mode 100644 tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 53156e6f74e16..4274d6e0a6adc 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -53,12 +53,13 @@ use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; +use crate::traits::util::elaborate; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, PredicateObligation, }; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, ErrorGuaranteed, IntoDiagArg, MultiSpan, @@ -458,11 +459,32 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // the error. If all of these fails, we fall back to a rather // general bit of code that displays the error information RegionResolutionError::ConcreteFailure(origin, sub, sup) => { + let extra_info = self.find_trait_object_relate_failure_reason( + generic_param_scope, + &origin, + sub, + sup, + ); let mut err = if sub.is_placeholder() || sup.is_placeholder() { self.report_placeholder_failure(origin, sub, sup) } else { self.report_concrete_failure(origin, sub, sup) }; + if let Some((primary_spans, relevant_bindings)) = extra_info { + if primary_spans.has_primary_spans() { + // We shorten the span from the whole field type to only the traits + // and lifetime bound that failed. + err.span(primary_spans); + } + if relevant_bindings.has_primary_spans() { + // Point at all the trait obligations for the lifetime that + // couldn't be met. + err.span_note( + relevant_bindings, + format!("unmet `{sub}` obligations introduced here"), + ); + } + } if let hir::def::DefKind::Impl { .. } = self.tcx.def_kind(generic_param_scope) && let Some(def_id) = @@ -471,28 +493,36 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Collect all the `Span`s corresponding to the predicates introducing // the `sub` lifetime that couldn't be met (sometimes `'static`) on // both the `trait` and the `impl`. - let spans: Vec = self - .tcx - .predicates_of(def_id) - .predicates - .iter() - .chain( - self.tcx.predicates_of(generic_param_scope).predicates.iter(), - ) - .filter_map(|(pred, span)| { - if let Some(ty::ClauseKind::TypeOutlives( - ty::OutlivesPredicate(pred_ty, r), - )) = pred.kind().no_bound_vars() - && r == sub - && let ty::Param(param) = pred_ty.kind() - && param.name == kw::SelfUpper - { - Some(*span) - } else { - None - } - }) - .collect(); + let spans: Vec = elaborate( + self.tcx, + self.tcx + .predicates_of(def_id) + .predicates + .iter() + .map(|(p, sp)| (p.as_predicate(), *sp)), + ) + .chain(elaborate( + self.tcx, + self.tcx + .predicates_of(generic_param_scope) + .predicates + .iter() + .map(|(p, sp)| (p.as_predicate(), *sp)), + )) + .filter_map(|(pred, pred_span)| { + if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() + && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + _pred_ty, + r, + )) = clause + && r.kind() == ty::ReStatic + { + Some(pred_span) + } else { + None + } + }) + .collect(); if !spans.is_empty() { let spans_len = spans.len(); @@ -2677,6 +2707,102 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() } } + /// If a field on a struct has a trait object with lifetime requirement that can't be satisfied + /// by one of the traits in the trait object, shorten the span from the whole field type to only + /// the relevant traits and the lifetime. We also collect the spans for the places where the + /// traits' obligations were introduced. + fn find_trait_object_relate_failure_reason( + &self, + generic_param_scope: LocalDefId, + origin: &SubregionOrigin<'tcx>, + sub: ty::Region<'tcx>, + sup: ty::Region<'tcx>, + ) -> Option<(MultiSpan, MultiSpan)> { + let infer::RelateRegionParamBound(span) = origin else { + return None; + }; + let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(item, _), .. })) = + self.tcx.hir().get_if_local(generic_param_scope.into()) + else { + return None; + }; + /// Collect all `hir::Ty<'_>` `Span`s for trait objects with the sup lifetime. + pub struct HirTraitObjectVisitor<'tcx>( + pub Vec<&'tcx hir::PolyTraitRef<'tcx>>, + pub ty::Region<'tcx>, + pub FxHashSet, + ); + impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'tcx> { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { + // Find all the trait objects that have the lifetime that was found. + if let hir::TyKind::TraitObject(poly_trait_refs, lt, _) = t.kind + && match (lt.res, self.1.kind()) { + ( + hir::LifetimeName::ImplicitObjectLifetimeDefault + | hir::LifetimeName::Static, + ty::RegionKind::ReStatic, + ) => true, + (hir::LifetimeName::Param(a), ty::RegionKind::ReEarlyParam(b)) => { + a.to_def_id() == b.def_id + } + _ => false, + } + { + for ptr in poly_trait_refs { + // We'll filter the traits later, after collection. + self.0.push(ptr); + } + // We want to keep a span to the lifetime bound on the trait object. + self.2.insert(lt.ident.span); + } + hir::intravisit::walk_ty(self, t); + } + } + let mut visitor = HirTraitObjectVisitor(vec![], sup, Default::default()); + for field in item.fields() { + if field.ty.span == *span { + // `span` points at the type of a field, we only want to look for trait objects in + // the field that failed. + visitor.visit_ty(field.ty); + } + } + + // The display of these spans will not change regardless or sorting. + #[allow(rustc::potential_query_instability)] + let mut primary_spans: Vec = visitor.2.into_iter().collect(); + let mut relevant_bindings: Vec = vec![]; + for ptr in visitor.0 { + if let Some(def_id) = ptr.trait_ref.trait_def_id() { + // Find the bounds on the trait with the lifetime that couldn't be met. + let bindings: Vec = elaborate( + self.tcx, + self.tcx + .predicates_of(def_id) + .predicates + .iter() + .map(|(p, sp)| (p.as_predicate(), *sp)), + ) + .filter_map(|(pred, pred_span)| { + if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() + && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_pred_ty, r)) = + clause + && r == sub + { + Some(pred_span) + } else { + None + } + }) + .collect(); + if !bindings.is_empty() { + primary_spans.push(ptr.span); + relevant_bindings.extend(bindings); + } + } + } + Some((primary_spans.into(), relevant_bindings.into())) + } + /// Determine whether an error associated with the given span and definition /// should be treated as being caused by the implicit `From` conversion /// within `?` desugaring. diff --git a/tests/ui/error-codes/E0478.stderr b/tests/ui/error-codes/E0478.stderr index 6ecde0e14e5e3..0227dabd2cd70 100644 --- a/tests/ui/error-codes/E0478.stderr +++ b/tests/ui/error-codes/E0478.stderr @@ -1,8 +1,8 @@ error[E0478]: lifetime bound not satisfied - --> $DIR/E0478.rs:4:12 + --> $DIR/E0478.rs:4:37 | LL | child: Box + 'SnowWhite>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'SnowWhite` as defined here --> $DIR/E0478.rs:3:22 diff --git a/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs b/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs new file mode 100644 index 0000000000000..47f1abd38a1c5 --- /dev/null +++ b/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs @@ -0,0 +1,6 @@ +use std::any::Any; + +struct Something<'a> { + broken: Box //~ ERROR lifetime bound not satisfied +} +fn main() {} diff --git a/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr b/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr new file mode 100644 index 0000000000000..5104c6faed037 --- /dev/null +++ b/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr @@ -0,0 +1,18 @@ +error[E0478]: lifetime bound not satisfied + --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:21 + | +LL | broken: Box + | ^^^ ^^ + | +note: lifetime parameter instantiated with the lifetime `'a` as defined here + --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18 + | +LL | struct Something<'a> { + | ^^ + = note: but lifetime parameter must outlive the static lifetime +note: unmet `'static` obligations introduced here + --> $SRC_DIR/core/src/any.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr b/tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr index b15d2affeea37..8cc67529951f5 100644 --- a/tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr +++ b/tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr @@ -5,10 +5,10 @@ LL | z: Box+'b+'c>, | ^^ error[E0478]: lifetime bound not satisfied - --> $DIR/region-bounds-on-objects-and-type-parameters.rs:21:8 + --> $DIR/region-bounds-on-objects-and-type-parameters.rs:21:23 | LL | z: Box+'b+'c>, - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^ | note: lifetime parameter instantiated with the lifetime `'b` as defined here --> $DIR/region-bounds-on-objects-and-type-parameters.rs:11:15 diff --git a/tests/ui/regions/regions-wf-trait-object.stderr b/tests/ui/regions/regions-wf-trait-object.stderr index 2099615082083..142be3924ebf8 100644 --- a/tests/ui/regions/regions-wf-trait-object.stderr +++ b/tests/ui/regions/regions-wf-trait-object.stderr @@ -1,8 +1,8 @@ error[E0478]: lifetime bound not satisfied - --> $DIR/regions-wf-trait-object.rs:7:8 + --> $DIR/regions-wf-trait-object.rs:7:29 | LL | x: Box+'b> - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^ | note: lifetime parameter instantiated with the lifetime `'b` as defined here --> $DIR/regions-wf-trait-object.rs:6:15 From 1c5993f9521ef49dc7573923ac76f46cc660a329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 21 Feb 2024 21:57:28 +0000 Subject: [PATCH 14/19] Reword sentence --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 2 +- ...int-at-lifetime-obligation-from-trait-in-trait-object.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 4274d6e0a6adc..d35af0bfcc358 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -481,7 +481,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // couldn't be met. err.span_note( relevant_bindings, - format!("unmet `{sub}` obligations introduced here"), + format!("`{sub}` requirement introduced here"), ); } } diff --git a/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr b/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr index 5104c6faed037..b1d0d2772f32e 100644 --- a/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr +++ b/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr @@ -10,7 +10,7 @@ note: lifetime parameter instantiated with the lifetime `'a` as defined here LL | struct Something<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime -note: unmet `'static` obligations introduced here +note: `'static` requirement introduced here --> $SRC_DIR/core/src/any.rs:LL:COL error: aborting due to 1 previous error From fefce2f2b7cdf05d034056b2a3db8b6b3449ebe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 21 Feb 2024 23:27:53 +0000 Subject: [PATCH 15/19] Account for lifetimes of assoc types in fields --- .../src/infer/error_reporting/mod.rs | 162 ++++++++++++------ .../unsatisfied-item-lifetime-bound.stderr | 27 ++- 2 files changed, 127 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index d35af0bfcc358..74660dbfd661b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2727,38 +2727,119 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return None; }; /// Collect all `hir::Ty<'_>` `Span`s for trait objects with the sup lifetime. - pub struct HirTraitObjectVisitor<'tcx>( - pub Vec<&'tcx hir::PolyTraitRef<'tcx>>, - pub ty::Region<'tcx>, - pub FxHashSet, - ); + pub struct HirTraitObjectVisitor<'tcx> { + pub expected_region: ty::Region<'tcx>, + pub found_region: ty::Region<'tcx>, + pub lifetime_spans: FxHashSet, + pub pred_spans: Vec, + pub tcx: TyCtxt<'tcx>, + } impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'tcx> { + fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { + if match (lt.res, self.expected_region.kind()) { + ( + hir::LifetimeName::ImplicitObjectLifetimeDefault + | hir::LifetimeName::Static, + ty::RegionKind::ReStatic, + ) => true, + (hir::LifetimeName::Param(a), ty::RegionKind::ReEarlyParam(b)) => { + a.to_def_id() == b.def_id + } + _ => false, + } { + // We want to keep a span to the lifetime bound on the trait object. + self.lifetime_spans.insert(lt.ident.span); + } + } fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { - // Find all the trait objects that have the lifetime that was found. - if let hir::TyKind::TraitObject(poly_trait_refs, lt, _) = t.kind - && match (lt.res, self.1.kind()) { - ( - hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Static, - ty::RegionKind::ReStatic, - ) => true, - (hir::LifetimeName::Param(a), ty::RegionKind::ReEarlyParam(b)) => { - a.to_def_id() == b.def_id + match t.kind { + // Find all the trait objects that have the lifetime that was found. + hir::TyKind::TraitObject(poly_trait_refs, lt, _) + if match (lt.res, self.expected_region.kind()) { + ( + hir::LifetimeName::ImplicitObjectLifetimeDefault + | hir::LifetimeName::Static, + ty::RegionKind::ReStatic, + ) => true, + (hir::LifetimeName::Param(a), ty::RegionKind::ReEarlyParam(b)) => { + a.to_def_id() == b.def_id + } + _ => false, + } => + { + for ptr in poly_trait_refs { + if let Some(def_id) = ptr.trait_ref.trait_def_id() { + // Find the bounds on the trait with the lifetime that couldn't be met. + let bindings: Vec = elaborate( + self.tcx, + self.tcx + .predicates_of(def_id) + .predicates + .iter() + .map(|(p, sp)| (p.as_predicate(), *sp)), + ) + .filter_map(|(pred, pred_span)| { + if let ty::PredicateKind::Clause(clause) = + pred.kind().skip_binder() + && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + _pred_ty, + r, + )) = clause + && r == self.found_region + { + Some(pred_span) + } else { + None + } + }) + .collect(); + if !bindings.is_empty() { + self.lifetime_spans.insert(ptr.span); + self.pred_spans.extend(bindings); + } + } } - _ => false, } - { - for ptr in poly_trait_refs { - // We'll filter the traits later, after collection. - self.0.push(ptr); + // Detect when an associated item is given a lifetime restriction that the + // definition of that associated item couldn't meet. + hir::TyKind::Path(hir::QPath::Resolved(Some(_), path)) => { + self.pred_spans = elaborate( + self.tcx, + self.tcx + .predicates_of(path.res.def_id()) + .predicates + .iter() + .map(|(p, sp)| (p.as_predicate(), *sp)), + ) + .filter_map(|(pred, pred_span)| { + match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( + ty::OutlivesPredicate( + // What should I filter this with? + _pred_ty, + r, + ), + )) if r == self.found_region => Some(pred_span), + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( + ty::OutlivesPredicate(_, r), + )) if r == self.found_region => Some(pred_span), + _ => None, + } + }) + .collect(); } - // We want to keep a span to the lifetime bound on the trait object. - self.2.insert(lt.ident.span); + _ => {} } hir::intravisit::walk_ty(self, t); } } - let mut visitor = HirTraitObjectVisitor(vec![], sup, Default::default()); + let mut visitor = HirTraitObjectVisitor { + expected_region: sup, + found_region: sub, + lifetime_spans: Default::default(), + pred_spans: vec![], + tcx: self.tcx, + }; for field in item.fields() { if field.ty.span == *span { // `span` points at the type of a field, we only want to look for trait objects in @@ -2767,40 +2848,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - // The display of these spans will not change regardless or sorting. #[allow(rustc::potential_query_instability)] - let mut primary_spans: Vec = visitor.2.into_iter().collect(); - let mut relevant_bindings: Vec = vec![]; - for ptr in visitor.0 { - if let Some(def_id) = ptr.trait_ref.trait_def_id() { - // Find the bounds on the trait with the lifetime that couldn't be met. - let bindings: Vec = elaborate( - self.tcx, - self.tcx - .predicates_of(def_id) - .predicates - .iter() - .map(|(p, sp)| (p.as_predicate(), *sp)), - ) - .filter_map(|(pred, pred_span)| { - if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() - && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_pred_ty, r)) = - clause - && r == sub - { - Some(pred_span) - } else { - None - } - }) - .collect(); - if !bindings.is_empty() { - primary_spans.push(ptr.span); - relevant_bindings.extend(bindings); - } - } - } - Some((primary_spans.into(), relevant_bindings.into())) + let primary_spans: Vec = visitor.lifetime_spans.into_iter().collect(); + Some((primary_spans.into(), visitor.pred_spans.into())) } /// Determine whether an error associated with the given span and definition diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr index 8d21b9172c87a..68f588eee5a72 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr @@ -32,10 +32,10 @@ LL | type Y<'a> = &'a () where 'a: 'static; | +++++++++++++++++ error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:14:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:14:20 | LL | f: ::Y<'a>, - | ^^^^^^^^^^^^^^^ + | ^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> $DIR/unsatisfied-item-lifetime-bound.rs:13:10 @@ -43,12 +43,17 @@ note: lifetime parameter instantiated with the lifetime `'a` as defined here LL | struct B<'a, T: for<'r> X = &'r ()>> { | ^^ = note: but lifetime parameter must outlive the static lifetime +note: `'static` requirement introduced here + --> $DIR/unsatisfied-item-lifetime-bound.rs:4:16 + | +LL | type Y<'a: 'static>; + | ^^^^^^^ error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:19:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:19:20 | LL | f: ::Y<'a>, - | ^^^^^^^^^^^^^^^ + | ^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> $DIR/unsatisfied-item-lifetime-bound.rs:18:10 @@ -56,12 +61,17 @@ note: lifetime parameter instantiated with the lifetime `'a` as defined here LL | struct C<'a, T: X> { | ^^ = note: but lifetime parameter must outlive the static lifetime +note: `'static` requirement introduced here + --> $DIR/unsatisfied-item-lifetime-bound.rs:4:16 + | +LL | type Y<'a: 'static>; + | ^^^^^^^ error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:24:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:24:21 | LL | f: <() as X>::Y<'a>, - | ^^^^^^^^^^^^^^^^ + | ^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> $DIR/unsatisfied-item-lifetime-bound.rs:23:10 @@ -69,6 +79,11 @@ note: lifetime parameter instantiated with the lifetime `'a` as defined here LL | struct D<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime +note: `'static` requirement introduced here + --> $DIR/unsatisfied-item-lifetime-bound.rs:4:16 + | +LL | type Y<'a: 'static>; + | ^^^^^^^ error: aborting due to 4 previous errors; 1 warning emitted From a539a0e9f0b656c4516e12e1697d9b2b0e43a8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 22 Feb 2024 00:13:28 +0000 Subject: [PATCH 16/19] Introduce `traits::util::elaborate_predicates_of` --- .../src/diagnostics/region_errors.rs | 52 +++---- .../src/infer/error_reporting/mod.rs | 131 +++++++----------- compiler/rustc_infer/src/traits/util.rs | 11 ++ 3 files changed, 87 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 6371858ebd18c..f1f1c422fc7b9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -19,13 +19,13 @@ use rustc_infer::infer::{ error_reporting::unexpected_hidden_region_diagnostic, BoundRegionConversionTime, NllRegionVariableOrigin, RelateParamBound, }; +use rustc_infer::traits::util::elaborate_predicates_of; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; -use rustc_trait_selection::traits; use crate::borrowck_errors; use crate::session_diagnostics::{ @@ -645,10 +645,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let def_id = instance.def_id(); let mut parent = tcx.parent(def_id); debug!(?def_id, ?parent); - let trait_preds = match tcx.def_kind(parent) { - hir::def::DefKind::Impl { .. } => { - tcx.trait_id_of_impl(parent).map_or(&[][..], |id| tcx.predicates_of(id).predicates) - } + let trait_preds: Vec<_> = match tcx.def_kind(parent) { + hir::def::DefKind::Impl { .. } => tcx + .trait_id_of_impl(parent) + .map_or(vec![], |id| elaborate_predicates_of(tcx, id).collect()), hir::def::DefKind::Trait => { let Some(ty) = args.get(0).and_then(|arg| arg.as_type()) else { return; @@ -660,9 +660,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let [def_id] = impls[..] { // The method we have is on the trait, but for `parent` we want to analyze the // relevant impl instead. - let preds = tcx.predicates_of(parent).predicates; + let preds = elaborate_predicates_of(tcx, parent); parent = def_id; - preds + preds.collect() } else { return; } @@ -688,30 +688,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // fn foo(&self) where Self: 'static {} // } // ``` - let mut predicates: Vec = traits::elaborate( - tcx, - tcx.predicates_of(def_id).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), - ) - .chain(traits::elaborate( - tcx, - tcx.predicates_of(parent).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), - )) - .chain(traits::elaborate(tcx, trait_preds.iter().map(|(p, sp)| (p.as_predicate(), *sp)))) - .filter_map(|(pred, pred_span)| { - if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() - && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r)) = clause - && r.kind() == ty::ReStatic - && (self.infcx.can_eq(self.param_env, ty, pred_ty) - || matches!( + let mut predicates: Vec = elaborate_predicates_of(tcx, def_id) + .chain(elaborate_predicates_of(tcx, parent)) + .chain(trait_preds) + .filter_map(|(pred, pred_span)| { + if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() + && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r)) = clause + && r.kind() == ty::ReStatic + && (self.infcx.can_eq(self.param_env, ty, pred_ty) + || matches!( pred_ty.kind(), ty::Param(name) if name.name == kw::SelfUpper)) - { - Some(pred_span) - } else { - None - } - }) - .collect(); + { + Some(pred_span) + } else { + None + } + }) + .collect(); debug!(?predicates); // Look at the receiver for `&'static self`, which introduces a `'static` obligation. diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 74660dbfd661b..ab83080efddda 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -53,7 +53,7 @@ use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; -use crate::traits::util::elaborate; +use crate::traits::util::elaborate_predicates_of; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, PredicateObligation, @@ -493,36 +493,26 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Collect all the `Span`s corresponding to the predicates introducing // the `sub` lifetime that couldn't be met (sometimes `'static`) on // both the `trait` and the `impl`. - let spans: Vec = elaborate( - self.tcx, - self.tcx - .predicates_of(def_id) - .predicates - .iter() - .map(|(p, sp)| (p.as_predicate(), *sp)), - ) - .chain(elaborate( - self.tcx, - self.tcx - .predicates_of(generic_param_scope) - .predicates - .iter() - .map(|(p, sp)| (p.as_predicate(), *sp)), - )) - .filter_map(|(pred, pred_span)| { - if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() - && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - _pred_ty, - r, - )) = clause - && r.kind() == ty::ReStatic - { - Some(pred_span) - } else { - None - } - }) - .collect(); + let spans: Vec = elaborate_predicates_of(self.tcx, def_id) + .chain(elaborate_predicates_of( + self.tcx, + generic_param_scope.into(), + )) + .filter_map(|(pred, pred_span)| { + if let ty::PredicateKind::Clause(clause) = + pred.kind().skip_binder() + && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + _pred_ty, + r, + )) = clause + && r.kind() == ty::ReStatic + { + Some(pred_span) + } else { + None + } + }) + .collect(); if !spans.is_empty() { let spans_len = spans.len(); @@ -2770,29 +2760,21 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { for ptr in poly_trait_refs { if let Some(def_id) = ptr.trait_ref.trait_def_id() { // Find the bounds on the trait with the lifetime that couldn't be met. - let bindings: Vec = elaborate( - self.tcx, - self.tcx - .predicates_of(def_id) - .predicates - .iter() - .map(|(p, sp)| (p.as_predicate(), *sp)), - ) - .filter_map(|(pred, pred_span)| { - if let ty::PredicateKind::Clause(clause) = - pred.kind().skip_binder() - && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - _pred_ty, - r, - )) = clause - && r == self.found_region - { - Some(pred_span) - } else { - None - } - }) - .collect(); + let bindings: Vec = elaborate_predicates_of(self.tcx, def_id) + .filter_map(|(pred, pred_span)| { + if let ty::PredicateKind::Clause(clause) = + pred.kind().skip_binder() + && let ty::ClauseKind::TypeOutlives( + ty::OutlivesPredicate(_pred_ty, r), + ) = clause + && r == self.found_region + { + Some(pred_span) + } else { + None + } + }) + .collect(); if !bindings.is_empty() { self.lifetime_spans.insert(ptr.span); self.pred_spans.extend(bindings); @@ -2803,30 +2785,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Detect when an associated item is given a lifetime restriction that the // definition of that associated item couldn't meet. hir::TyKind::Path(hir::QPath::Resolved(Some(_), path)) => { - self.pred_spans = elaborate( - self.tcx, - self.tcx - .predicates_of(path.res.def_id()) - .predicates - .iter() - .map(|(p, sp)| (p.as_predicate(), *sp)), - ) - .filter_map(|(pred, pred_span)| { - match pred.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( - ty::OutlivesPredicate( - // What should I filter this with? - _pred_ty, - r, - ), - )) if r == self.found_region => Some(pred_span), - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( - ty::OutlivesPredicate(_, r), - )) if r == self.found_region => Some(pred_span), - _ => None, - } - }) - .collect(); + self.pred_spans = elaborate_predicates_of(self.tcx, path.res.def_id()) + .filter_map(|(pred, pred_span)| { + match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( + ty::OutlivesPredicate( + // What should I filter this with? + _pred_ty, + r, + ), + )) if r == self.found_region => Some(pred_span), + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( + ty::OutlivesPredicate(_, r), + )) if r == self.found_region => Some(pred_span), + _ => None, + } + }) + .collect(); } _ => {} } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index e9df0505cbbdc..8bbf3ae240bd4 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -4,6 +4,7 @@ use crate::infer::outlives::components::{push_outlives_components, Component}; use crate::traits::{self, Obligation, PredicateObligation}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; +use rustc_span::def_id::DefId; use rustc_span::symbol::Ident; use rustc_span::Span; @@ -235,6 +236,16 @@ pub fn elaborate<'tcx, O: Elaboratable<'tcx>>( elaborator } +pub fn elaborate_predicates_of<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> Elaborator<'tcx, (ty::Predicate<'tcx>, Span)> { + elaborate( + tcx, + tcx.predicates_of(def_id).predicates.iter().map(|(p, sp)| (p.as_predicate(), *sp)), + ) +} + impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { fn extend_deduped(&mut self, obligations: impl IntoIterator) { // Only keep those bounds that we haven't already seen. From 64b8cc9dd0d57e77cf9d46e0cb3890ddff8b5e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 22 Feb 2024 00:16:56 +0000 Subject: [PATCH 17/19] Introduce `traits::util::filter_predicates` --- .../src/diagnostics/region_errors.rs | 20 +++----- .../src/infer/error_reporting/mod.rs | 49 +++---------------- compiler/rustc_infer/src/traits/util.rs | 21 ++++++++ 3 files changed, 33 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index f1f1c422fc7b9..a842255f7c63a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -19,7 +19,7 @@ use rustc_infer::infer::{ error_reporting::unexpected_hidden_region_diagnostic, BoundRegionConversionTime, NllRegionVariableOrigin, RelateParamBound, }; -use rustc_infer::traits::util::elaborate_predicates_of; +use rustc_infer::traits::util::{elaborate_predicates_of, filter_predicates}; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::traits::ObligationCauseCode; @@ -691,20 +691,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut predicates: Vec = elaborate_predicates_of(tcx, def_id) .chain(elaborate_predicates_of(tcx, parent)) .chain(trait_preds) - .filter_map(|(pred, pred_span)| { - if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() - && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r)) = clause - && r.kind() == ty::ReStatic - && (self.infcx.can_eq(self.param_env, ty, pred_ty) - || matches!( + .filter_map(filter_predicates(tcx.lifetimes.re_static, |pred_ty| { + self.infcx.can_eq(self.param_env, ty, pred_ty) + || matches!( pred_ty.kind(), - ty::Param(name) if name.name == kw::SelfUpper)) - { - Some(pred_span) - } else { - None - } - }) + ty::Param(name) if name.name == kw::SelfUpper) + })) .collect(); debug!(?predicates); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index ab83080efddda..7e12e48f16311 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -53,7 +53,7 @@ use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; -use crate::traits::util::elaborate_predicates_of; +use crate::traits::util::{elaborate_predicates_of, filter_predicates}; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, PredicateObligation, @@ -498,20 +498,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.tcx, generic_param_scope.into(), )) - .filter_map(|(pred, pred_span)| { - if let ty::PredicateKind::Clause(clause) = - pred.kind().skip_binder() - && let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - _pred_ty, - r, - )) = clause - && r.kind() == ty::ReStatic - { - Some(pred_span) - } else { - None - } - }) + .filter_map(filter_predicates(self.tcx.lifetimes.re_static, |_| { + true + })) .collect(); if !spans.is_empty() { @@ -2761,19 +2750,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let Some(def_id) = ptr.trait_ref.trait_def_id() { // Find the bounds on the trait with the lifetime that couldn't be met. let bindings: Vec = elaborate_predicates_of(self.tcx, def_id) - .filter_map(|(pred, pred_span)| { - if let ty::PredicateKind::Clause(clause) = - pred.kind().skip_binder() - && let ty::ClauseKind::TypeOutlives( - ty::OutlivesPredicate(_pred_ty, r), - ) = clause - && r == self.found_region - { - Some(pred_span) - } else { - None - } - }) + .filter_map(filter_predicates(self.found_region, |_| true)) .collect(); if !bindings.is_empty() { self.lifetime_spans.insert(ptr.span); @@ -2786,21 +2763,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // definition of that associated item couldn't meet. hir::TyKind::Path(hir::QPath::Resolved(Some(_), path)) => { self.pred_spans = elaborate_predicates_of(self.tcx, path.res.def_id()) - .filter_map(|(pred, pred_span)| { - match pred.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( - ty::OutlivesPredicate( - // What should I filter this with? - _pred_ty, - r, - ), - )) if r == self.found_region => Some(pred_span), - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( - ty::OutlivesPredicate(_, r), - )) if r == self.found_region => Some(pred_span), - _ => None, - } - }) + .filter_map(filter_predicates(self.found_region, |_| true)) .collect(); } _ => {} diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 8bbf3ae240bd4..2ed96f2f7db4f 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -246,6 +246,27 @@ pub fn elaborate_predicates_of<'tcx>( ) } +pub fn filter_predicates<'tcx>( + region: ty::Region<'tcx>, + check_ty: impl Fn(Ty<'tcx>) -> bool, +) -> impl Fn((ty::Predicate<'tcx>, Span)) -> Option { + move |(pred, span)| { + let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() else { + return None; + }; + match clause { + ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r)) + if r == region && check_ty(pred_ty) => + { + Some(span) + } + ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(_, r)) if r == region => { + Some(span) + } + _ => None, + } + } +} impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { fn extend_deduped(&mut self, obligations: impl IntoIterator) { // Only keep those bounds that we haven't already seen. From 014daaaeffb10eb98e36950a93975a2d5c58b9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 22 Feb 2024 02:55:08 +0000 Subject: [PATCH 18/19] Tweak E0478 ``` error[E0478]: lifetime bound not satisfied --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:27 | LL | broken: Box | --- ^^ lifetime bound not satisfied | | | this requires `'static` | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18 | LL | struct Something<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime note: `'static` requirement introduced here --> $SRC_DIR/core/src/any.rs:LL:COL ``` --- .../src/infer/error_reporting/mod.rs | 41 +++++++++++++------ tests/ui/error-codes/E0478.stderr | 2 +- .../unsatisfied-item-lifetime-bound.stderr | 6 +-- ...ligation-from-trait-in-trait-object.stderr | 6 ++- ...unds-on-objects-and-type-parameters.stderr | 2 +- .../ui/regions/regions-wf-trait-object.stderr | 2 +- 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 7e12e48f16311..80e4ead7ae0e0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -59,7 +59,7 @@ use crate::traits::{ PredicateObligation, }; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, ErrorGuaranteed, IntoDiagArg, MultiSpan, @@ -2709,7 +2709,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub struct HirTraitObjectVisitor<'tcx> { pub expected_region: ty::Region<'tcx>, pub found_region: ty::Region<'tcx>, - pub lifetime_spans: FxHashSet, + pub primary_spans: Vec, + pub secondary_spans: Vec, pub pred_spans: Vec, pub tcx: TyCtxt<'tcx>, } @@ -2727,7 +2728,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { _ => false, } { // We want to keep a span to the lifetime bound on the trait object. - self.lifetime_spans.insert(lt.ident.span); + self.primary_spans.push(lt.ident.span); } } fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { @@ -2753,7 +2754,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .filter_map(filter_predicates(self.found_region, |_| true)) .collect(); if !bindings.is_empty() { - self.lifetime_spans.insert(ptr.span); + self.secondary_spans.push(ptr.span); self.pred_spans.extend(bindings); } } @@ -2761,10 +2762,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } // Detect when an associated item is given a lifetime restriction that the // definition of that associated item couldn't meet. - hir::TyKind::Path(hir::QPath::Resolved(Some(_), path)) => { - self.pred_spans = elaborate_predicates_of(self.tcx, path.res.def_id()) - .filter_map(filter_predicates(self.found_region, |_| true)) - .collect(); + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { + self.pred_spans.extend( + elaborate_predicates_of(self.tcx, path.res.def_id()) + .filter_map(filter_predicates(self.found_region, |_| true)) + .collect::>(), + ); } _ => {} } @@ -2774,7 +2777,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut visitor = HirTraitObjectVisitor { expected_region: sup, found_region: sub, - lifetime_spans: Default::default(), + primary_spans: vec![], + secondary_spans: vec![], pred_spans: vec![], tcx: self.tcx, }; @@ -2786,9 +2790,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - #[allow(rustc::potential_query_instability)] - let primary_spans: Vec = visitor.lifetime_spans.into_iter().collect(); - Some((primary_spans.into(), visitor.pred_spans.into())) + visitor.primary_spans.sort(); + let mut primary_span: MultiSpan = visitor.primary_spans.clone().into(); + if let Some(last) = visitor.primary_spans.iter().rev().next() { + primary_span.push_span_label( + *last, + format!( + "lifetime bound{s} not satisfied", + s = pluralize!(visitor.primary_spans.len()) + ), + ); + } + + for span in visitor.secondary_spans { + primary_span.push_span_label(span, format!("this requires `{sub}`")); + } + Some((primary_span, visitor.pred_spans.into())) } /// Determine whether an error associated with the given span and definition diff --git a/tests/ui/error-codes/E0478.stderr b/tests/ui/error-codes/E0478.stderr index 0227dabd2cd70..e6b03df16b109 100644 --- a/tests/ui/error-codes/E0478.stderr +++ b/tests/ui/error-codes/E0478.stderr @@ -2,7 +2,7 @@ error[E0478]: lifetime bound not satisfied --> $DIR/E0478.rs:4:37 | LL | child: Box + 'SnowWhite>, - | ^^^^^^^^^^ + | ^^^^^^^^^^ lifetime bound not satisfied | note: lifetime parameter instantiated with the lifetime `'SnowWhite` as defined here --> $DIR/E0478.rs:3:22 diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr index 68f588eee5a72..70b94176d6576 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr @@ -35,7 +35,7 @@ error[E0478]: lifetime bound not satisfied --> $DIR/unsatisfied-item-lifetime-bound.rs:14:20 | LL | f: ::Y<'a>, - | ^^ + | ^^ lifetime bound not satisfied | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> $DIR/unsatisfied-item-lifetime-bound.rs:13:10 @@ -53,7 +53,7 @@ error[E0478]: lifetime bound not satisfied --> $DIR/unsatisfied-item-lifetime-bound.rs:19:20 | LL | f: ::Y<'a>, - | ^^ + | ^^ lifetime bound not satisfied | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> $DIR/unsatisfied-item-lifetime-bound.rs:18:10 @@ -71,7 +71,7 @@ error[E0478]: lifetime bound not satisfied --> $DIR/unsatisfied-item-lifetime-bound.rs:24:21 | LL | f: <() as X>::Y<'a>, - | ^^ + | ^^ lifetime bound not satisfied | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> $DIR/unsatisfied-item-lifetime-bound.rs:23:10 diff --git a/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr b/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr index b1d0d2772f32e..f2b4feffd5d5a 100644 --- a/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr +++ b/tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.stderr @@ -1,8 +1,10 @@ error[E0478]: lifetime bound not satisfied - --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:21 + --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:27 | LL | broken: Box - | ^^^ ^^ + | --- ^^ lifetime bound not satisfied + | | + | this requires `'static` | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18 diff --git a/tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr b/tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr index 8cc67529951f5..c1d5c4d908bac 100644 --- a/tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr +++ b/tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr @@ -8,7 +8,7 @@ error[E0478]: lifetime bound not satisfied --> $DIR/region-bounds-on-objects-and-type-parameters.rs:21:23 | LL | z: Box+'b+'c>, - | ^^ + | ^^ lifetime bound not satisfied | note: lifetime parameter instantiated with the lifetime `'b` as defined here --> $DIR/region-bounds-on-objects-and-type-parameters.rs:11:15 diff --git a/tests/ui/regions/regions-wf-trait-object.stderr b/tests/ui/regions/regions-wf-trait-object.stderr index 142be3924ebf8..df08b67048f6c 100644 --- a/tests/ui/regions/regions-wf-trait-object.stderr +++ b/tests/ui/regions/regions-wf-trait-object.stderr @@ -2,7 +2,7 @@ error[E0478]: lifetime bound not satisfied --> $DIR/regions-wf-trait-object.rs:7:29 | LL | x: Box+'b> - | ^^ + | ^^ lifetime bound not satisfied | note: lifetime parameter instantiated with the lifetime `'b` as defined here --> $DIR/regions-wf-trait-object.rs:6:15 From 87666585b76c4176fced47c4e5c489a33f3cf0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 20 Mar 2024 19:03:15 +0000 Subject: [PATCH 19/19] Remove `FIXME` after rebase and add test --- .../src/diagnostics/region_errors.rs | 6 ------ tests/ui/lifetimes/static-impl-obligation.rs | 12 ++++++++++++ .../ui/lifetimes/static-impl-obligation.stderr | 18 ++++++++++++++++-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index a842255f7c63a..74ea8ec20f6b1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -675,12 +675,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if self.to_error_region(outlived_fr) != Some(tcx.lifetimes.re_static) { return; } - // FIXME: there's a case that's yet to be handled: `impl dyn Trait + '_ where Self: '_` - // causes *two* errors to be produded, one about `where Self: '_` not being allowed, - // and the regular error with no additional information about "lifetime may not live - // long enough for `'static`" without mentioning where it came from. This is because - // our error recovery fallback is indeed `ReStatic`. We should at some point introduce - // a `ReError` instead to avoid this and other similar issues. // Look for `'static` bounds in the generics of the method and the `impl`. // ``` diff --git a/tests/ui/lifetimes/static-impl-obligation.rs b/tests/ui/lifetimes/static-impl-obligation.rs index 69178a7375f63..a1cd60d66d184 100644 --- a/tests/ui/lifetimes/static-impl-obligation.rs +++ b/tests/ui/lifetimes/static-impl-obligation.rs @@ -276,4 +276,16 @@ mod w { y.hello(); //~ ERROR lifetime may not live long enough } } +mod x { + trait Foo {} + impl<'a> Foo for &'a u32 {} + impl dyn Foo + '_ where Self: '_ { //~ ERROR `'_` cannot be used here + fn hello(&self) {} + + } + fn convert<'a>(x: &'a &'a u32) { + let y: &dyn Foo = x; //~ ERROR lifetime may not live long enough + y.hello(); + } +} fn main() {} diff --git a/tests/ui/lifetimes/static-impl-obligation.stderr b/tests/ui/lifetimes/static-impl-obligation.stderr index 0eba240eb51d4..150662931b26c 100644 --- a/tests/ui/lifetimes/static-impl-obligation.stderr +++ b/tests/ui/lifetimes/static-impl-obligation.stderr @@ -1,3 +1,9 @@ +error[E0637]: `'_` cannot be used here + --> $DIR/static-impl-obligation.rs:282:35 + | +LL | impl dyn Foo + '_ where Self: '_ { + | ^^ `'_` is a reserved lifetime name + error[E0478]: lifetime bound not satisfied --> $DIR/static-impl-obligation.rs:222:10 | @@ -453,7 +459,15 @@ note: the `impl` on `dyn w::Foo` has a `'static` lifetime requirement LL | trait Trait where Self: 'static { fn hello(&self) {} } | ^^^^^^^ -error: aborting due to 25 previous errors +error: lifetime may not live long enough + --> $DIR/static-impl-obligation.rs:287:27 + | +LL | fn convert<'a>(x: &'a &'a u32) { + | -- lifetime `'a` defined here +LL | let y: &dyn Foo = x; + | ^ cast requires that `'a` must outlive `'static` + +error: aborting due to 27 previous errors -Some errors have detailed explanations: E0478, E0521. +Some errors have detailed explanations: E0478, E0521, E0637. For more information about an error, try `rustc --explain E0478`.