From 3cd5ad5cd7d85fc36c3696e3022bef5c5af088d2 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Thu, 15 Jul 2021 10:03:39 -0400 Subject: [PATCH 1/2] Better diagnostics when mismatched types due to implict static lifetime --- .../src/infer/error_reporting/mod.rs | 18 ++-- .../mismatched_static_lifetime.rs | 84 +++++++++++++++++++ .../error_reporting/nice_region_error/mod.rs | 2 + .../nice_region_error/static_impl_trait.rs | 10 ++- compiler/rustc_middle/src/traits/mod.rs | 3 + .../src/traits/error_reporting/suggestions.rs | 3 +- .../src/traits/select/mod.rs | 8 +- ...e-78113-lifetime-mismatch-dyn-trait-box.rs | 19 +++++ ...113-lifetime-mismatch-dyn-trait-box.stderr | 28 +++++++ .../wf/wf-in-foreign-fn-decls-issue-80468.rs | 2 +- .../wf-in-foreign-fn-decls-issue-80468.stderr | 15 ++-- 11 files changed, 168 insertions(+), 24 deletions(-) create mode 100644 compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs create mode 100644 src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs create mode 100644 src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index e3a79fe265330..670129937be2b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1590,17 +1590,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } }; if let Some((expected, found)) = expected_found { - let expected_label = match exp_found { - Mismatch::Variable(ef) => ef.expected.prefix_string(self.tcx), - Mismatch::Fixed(s) => s.into(), - }; - let found_label = match exp_found { - Mismatch::Variable(ef) => ef.found.prefix_string(self.tcx), - Mismatch::Fixed(s) => s.into(), - }; - let exp_found = match exp_found { - Mismatch::Variable(exp_found) => Some(exp_found), - Mismatch::Fixed(_) => None, + let (expected_label, found_label, exp_found) = match exp_found { + Mismatch::Variable(ef) => ( + ef.expected.prefix_string(self.tcx), + ef.found.prefix_string(self.tcx), + Some(ef), + ), + Mismatch::Fixed(s) => (s.into(), s.into(), None), }; match (&terr, expected == found) { (TypeError::Sorts(values), extra) => { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs new file mode 100644 index 0000000000000..6d3ba770f6a0f --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -0,0 +1,84 @@ +//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate +//! to hold. + +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::error_reporting::note_and_explain_region; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::{SubregionOrigin, TypeTrace}; +use crate::traits::ObligationCauseCode; +use rustc_errors::{Applicability, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::intravisit::Visitor; +use rustc_middle::ty::{self, TypeVisitor}; +use rustc_span::MultiSpan; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option { + let error = self.error.as_ref()?; + debug!("try_report_mismatched_static_lifetime {:?}", error); + + let (origin, sub, sup) = match error.clone() { + RegionResolutionError::ConcreteFailure(origin, sub, sup) => (origin, sub, sup), + _ => return None, + }; + if *sub != ty::RegionKind::ReStatic { + return None; + } + let cause = match origin { + SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) => cause, + _ => return None, + }; + let (parent, impl_def_id) = match &cause.code { + ObligationCauseCode::MatchImpl(parent, impl_def_id) => (parent, impl_def_id), + _ => return None, + }; + let binding_span = match **parent { + ObligationCauseCode::BindingObligation(_def_id, binding_span) => binding_span, + _ => return None, + }; + let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type"); + // FIXME: we should point at the lifetime + let mut multi_span: MultiSpan = vec![binding_span].into(); + multi_span + .push_span_label(binding_span, "introduces a `'static` lifetime requirement".into()); + err.span_note(multi_span, "because this has an unmet lifetime requirement"); + note_and_explain_region(self.tcx(), &mut err, "...", sup, "..."); + if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { + let ty = self.tcx().type_of(*impl_def_id); + let mut v = super::static_impl_trait::TraitObjectVisitor(vec![]); + v.visit_ty(ty); + let matching_def_ids = v.0; + + let impl_self_ty = match impl_node { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + }) => self_ty, + _ => bug!("Node not an impl."), + }; + + for matching_def_id in matching_def_ids { + let mut hir_v = + super::static_impl_trait::HirTraitObjectVisitor(vec![], matching_def_id); + hir_v.visit_ty(&impl_self_ty); + + let mut multi_span: MultiSpan = hir_v.0.clone().into(); + for span in &hir_v.0 { + multi_span.push_span_label( + *span, + "this has an implicit `'static` lifetime requirement".to_string(), + ); + err.span_suggestion_verbose( + span.shrink_to_hi(), + "consider relaxing the implicit `'static` requirement", + " + '_".to_string(), + Applicability::MaybeIncorrect, + ); + } + err.span_note(multi_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + } + } + err.emit(); + Some(ErrorReported) + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index e20436690b3aa..3f27bf67b59a9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -7,6 +7,7 @@ use rustc_span::source_map::Span; mod different_lifetimes; pub mod find_anon_type; +mod mismatched_static_lifetime; mod named_anon_conflict; mod placeholder_error; mod static_impl_trait; @@ -58,6 +59,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { .or_else(|| self.try_report_impl_not_conforming_to_trait()) .or_else(|| self.try_report_anon_anon_conflict()) .or_else(|| self.try_report_static_impl_trait()) + .or_else(|| self.try_report_mismatched_static_lifetime()) } pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 1e926989263c9..a164bc41d1499 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -185,7 +185,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin { - if let ObligationCauseCode::ItemObligation(item_def_id) = cause.code { + let code = match &cause.code { + ObligationCauseCode::MatchImpl(parent, ..) => &**parent, + _ => &cause.code, + }; + if let ObligationCauseCode::ItemObligation(item_def_id) = *code { // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` // lifetime as above, but called using a fully-qualified path to the method: // `Foo::qux(bar)`. @@ -468,7 +472,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime. -struct TraitObjectVisitor(Vec); +pub(super) struct TraitObjectVisitor(pub(super) Vec); impl TypeVisitor<'_> for TraitObjectVisitor { fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow { @@ -485,7 +489,7 @@ impl TypeVisitor<'_> for TraitObjectVisitor { } /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime. -struct HirTraitObjectVisitor(Vec, DefId); +pub(super) struct HirTraitObjectVisitor(pub(super) Vec, pub(super) DefId); impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor { type Map = ErasedMap<'tcx>; diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 221dac9ca035d..f951e43fbfa35 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -333,6 +333,9 @@ pub enum ObligationCauseCode<'tcx> { /// This is purely for diagnostic purposes - it is always /// correct to use `MiscObligation` instead WellFormed(Option), + + /// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against. + MatchImpl(Lrc>, DefId), } impl ObligationCauseCode<'_> { 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 adeb1d58d1ece..50a18ce0ebfff 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1903,7 +1903,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::UnifyReceiver(..) | ObligationCauseCode::OpaqueType | ObligationCauseCode::MiscObligation - | ObligationCauseCode::WellFormed(..) => {} + | ObligationCauseCode::WellFormed(..) + | ObligationCauseCode::MatchImpl(..) => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 564c63ef30cb6..1bdc8b34cf6ba 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1903,9 +1903,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?impl_trait_ref, ?placeholder_obligation_trait_ref); + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObligationCauseCode::MatchImpl(Lrc::new(obligation.cause.code.clone()), impl_def_id), + ); + let InferOk { obligations, .. } = self .infcx - .at(&obligation.cause, obligation.param_env) + .at(&cause, obligation.param_env) .eq(placeholder_obligation_trait_ref, impl_trait_ref) .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; nested_obligations.extend(obligations); diff --git a/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs new file mode 100644 index 0000000000000..8f40a03ae6f5b --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs @@ -0,0 +1,19 @@ +// Test for diagnostics when we have mismatched lifetime due to implict 'static lifetime in GATs + +// check-fail + +#![feature(generic_associated_types)] + +pub trait A {} +impl A for &dyn A {} +impl A for Box {} + +pub trait B { + type T<'a>: A; +} + +impl B for () { + type T<'a> = Box; //~ incompatible lifetime on type +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.stderr b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.stderr new file mode 100644 index 0000000000000..b77b58284eefb --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.stderr @@ -0,0 +1,28 @@ +error: incompatible lifetime on type + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:16:5 + | +LL | type T<'a> = Box; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: because this has an unmet lifetime requirement + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:12:17 + | +LL | type T<'a>: A; + | ^ introduces a `'static` lifetime requirement +note: ...the lifetime `'a` as defined on the associated item at 16:12... + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:16:12 + | +LL | type T<'a> = Box; + | ^^ +note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:9:20 + | +LL | impl A for Box {} + | ^ this has an implicit `'static` lifetime requirement +help: consider relaxing the implicit `'static` requirement + | +LL | impl A for Box {} + | ^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs index 8386959cfb3a8..4fcf8f403bbb6 100644 --- a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs +++ b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs @@ -13,5 +13,5 @@ pub struct Ref<'a>(&'a u8); impl Trait for Ref {} //~ ERROR: implicit elided lifetime not allowed here extern "C" { - pub fn repro(_: Wrapper); //~ ERROR: mismatched types + pub fn repro(_: Wrapper); //~ ERROR: incompatible lifetime on type } diff --git a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr index bb839d0a5eca6..48c9f362fe224 100644 --- a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr +++ b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr @@ -4,21 +4,22 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl Trait for Ref {} | ^^^- help: indicate the anonymous lifetime: `<'_>` -error[E0308]: mismatched types +error: incompatible lifetime on type --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21 | LL | pub fn repro(_: Wrapper); - | ^^^^^^^^^^^^ lifetime mismatch + | ^^^^^^^^^^^^ | - = note: expected trait `Trait` - found trait `Trait` -note: the anonymous lifetime #1 defined on the method body at 16:5... +note: because this has an unmet lifetime requirement + --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:8:23 + | +LL | pub struct Wrapper(T); + | ^^^^^ introduces a `'static` lifetime requirement +note: ...the anonymous lifetime #1 defined on the method body at 16:5... --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:5 | LL | pub fn repro(_: Wrapper); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...does not necessarily outlive the static lifetime error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. From ae024919845a44473c22b8c3f1dfa075c9c5c75d Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 19 Jul 2021 23:44:35 -0400 Subject: [PATCH 2/2] Better errors when we don't have implicit statics in trait objects --- compiler/rustc_ast_lowering/src/path.rs | 1 + .../mismatched_static_lifetime.rs | 47 ++++++++---- .../nice_region_error/static_impl_trait.rs | 36 +++++----- .../async-await/async-fn-path-elision.stderr | 2 + ...e-78113-lifetime-mismatch-dyn-trait-box.rs | 21 ++++++ ...113-lifetime-mismatch-dyn-trait-box.stderr | 71 +++++++++++++++++-- .../path-elided.stderr | 2 + .../trait-elided.stderr | 2 + src/test/ui/issues/issue-10412.stderr | 2 + .../wf-in-foreign-fn-decls-issue-80468.stderr | 9 ++- 10 files changed, 155 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index fe9f1fb20f056..55173c6f8696e 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -336,6 +336,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { insertion_sp, suggestion, ); + err.note("assuming a `'static` lifetime..."); err.emit(); } AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 6d3ba770f6a0f..cca195417270b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -6,6 +6,7 @@ use crate::infer::error_reporting::note_and_explain_region; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::ObligationCauseCode; +use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::{Applicability, ErrorReported}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; @@ -42,13 +43,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { multi_span .push_span_label(binding_span, "introduces a `'static` lifetime requirement".into()); err.span_note(multi_span, "because this has an unmet lifetime requirement"); - note_and_explain_region(self.tcx(), &mut err, "...", sup, "..."); + note_and_explain_region(self.tcx(), &mut err, "", sup, "..."); if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { - let ty = self.tcx().type_of(*impl_def_id); - let mut v = super::static_impl_trait::TraitObjectVisitor(vec![]); - v.visit_ty(ty); - let matching_def_ids = v.0; + // If an impl is local, then maybe this isn't what they want. Try to + // be as helpful as possible with implicit lifetimes. + // First, let's get the hir self type of the impl let impl_self_ty = match impl_node { hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { self_ty, .. }), @@ -57,17 +57,32 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { _ => bug!("Node not an impl."), }; - for matching_def_id in matching_def_ids { + // Next, let's figure out the set of trait objects with implict static bounds + let ty = self.tcx().type_of(*impl_def_id); + let mut v = super::static_impl_trait::TraitObjectVisitor(FxHashSet::default()); + v.visit_ty(ty); + let mut traits = vec![]; + for matching_def_id in v.0 { let mut hir_v = - super::static_impl_trait::HirTraitObjectVisitor(vec![], matching_def_id); + super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id); hir_v.visit_ty(&impl_self_ty); + } - let mut multi_span: MultiSpan = hir_v.0.clone().into(); - for span in &hir_v.0 { - multi_span.push_span_label( - *span, - "this has an implicit `'static` lifetime requirement".to_string(), - ); + if traits.is_empty() { + // If there are no trait object traits to point at, either because + // there aren't trait objects or because none are implicit, then just + // write a single note on the impl itself. + + let impl_span = self.tcx().def_span(*impl_def_id); + err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + } else { + // Otherwise, point at all implicit static lifetimes + + err.note("...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + for span in &traits { + err.span_note(*span, "this has an implicit `'static` lifetime requirement"); + // It would be nice to put this immediately under the above note, but they get + // pushed to the end. err.span_suggestion_verbose( span.shrink_to_hi(), "consider relaxing the implicit `'static` requirement", @@ -75,8 +90,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - err.span_note(multi_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); } + } else { + // Otherwise just point out the impl. + + let impl_span = self.tcx().def_span(*impl_def_id); + err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); } err.emit(); Some(ErrorReported) diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index a164bc41d1499..fde4ec05ffc86 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -4,6 +4,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; +use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor}; @@ -193,13 +194,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` // lifetime as above, but called using a fully-qualified path to the method: // `Foo::qux(bar)`. - let mut v = TraitObjectVisitor(vec![]); + let mut v = TraitObjectVisitor(FxHashSet::default()); v.visit_ty(param.param_ty); if let Some((ident, self_ty)) = - self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0[..]) + self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0) { - if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0[..], ident, self_ty) - { + if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) { override_error_code = Some(ident); } } @@ -340,7 +340,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { fn get_impl_ident_and_self_ty_from_trait( &self, def_id: DefId, - trait_objects: &[DefId], + trait_objects: &FxHashSet, ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> { let tcx = self.tcx(); match tcx.hir().get_if_local(def_id) { @@ -377,9 +377,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // multiple `impl`s for the same trait like // `impl Foo for Box` and `impl Foo for dyn Bar`. // In that case, only the first one will get suggestions. - let mut hir_v = HirTraitObjectVisitor(vec![], *did); + let mut traits = vec![]; + let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); hir_v.visit_ty(self_ty); - !hir_v.0.is_empty() + !traits.is_empty() }) => { Some(self_ty) @@ -421,33 +422,34 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { _ => return false, }; - let mut v = TraitObjectVisitor(vec![]); + let mut v = TraitObjectVisitor(FxHashSet::default()); v.visit_ty(ty); // Get the `Ident` of the method being called and the corresponding `impl` (to point at // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called). let (ident, self_ty) = - match self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0[..]) { + match self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0) { Some((ident, self_ty)) => (ident, self_ty), None => return false, }; // Find the trait object types in the argument, so we point at *only* the trait object. - self.suggest_constrain_dyn_trait_in_impl(err, &v.0[..], ident, self_ty) + self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty) } fn suggest_constrain_dyn_trait_in_impl( &self, err: &mut DiagnosticBuilder<'_>, - found_dids: &[DefId], + found_dids: &FxHashSet, ident: Ident, self_ty: &hir::Ty<'_>, ) -> bool { let mut suggested = false; for found_did in found_dids { - let mut hir_v = HirTraitObjectVisitor(vec![], *found_did); + let mut traits = vec![]; + let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); hir_v.visit_ty(&self_ty); - for span in &hir_v.0 { + for span in &traits { let mut multi_span: MultiSpan = vec![*span].into(); multi_span.push_span_label( *span, @@ -472,14 +474,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime. -pub(super) struct TraitObjectVisitor(pub(super) Vec); +pub(super) struct TraitObjectVisitor(pub(super) FxHashSet); impl TypeVisitor<'_> for TraitObjectVisitor { fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow { match t.kind() { ty::Dynamic(preds, RegionKind::ReStatic) => { if let Some(def_id) = preds.principal_def_id() { - self.0.push(def_id); + self.0.insert(def_id); } ControlFlow::CONTINUE } @@ -489,9 +491,9 @@ impl TypeVisitor<'_> for TraitObjectVisitor { } /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime. -pub(super) struct HirTraitObjectVisitor(pub(super) Vec, pub(super) DefId); +pub(super) struct HirTraitObjectVisitor<'a>(pub(super) &'a mut Vec, pub(super) DefId); -impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor { +impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { diff --git a/src/test/ui/async-await/async-fn-path-elision.stderr b/src/test/ui/async-await/async-fn-path-elision.stderr index 9694742200ef0..36fb73a8dde24 100644 --- a/src/test/ui/async-await/async-fn-path-elision.stderr +++ b/src/test/ui/async-await/async-fn-path-elision.stderr @@ -3,6 +3,8 @@ error[E0726]: implicit elided lifetime not allowed here | LL | async fn error(lt: HasLifetime) { | ^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + | + = note: assuming a `'static` lifetime... error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs index 8f40a03ae6f5b..850d83be6845d 100644 --- a/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs +++ b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs @@ -13,7 +13,28 @@ pub trait B { } impl B for () { + // `'a` doesn't match implicit `'static`: suggest `'_` type T<'a> = Box; //~ incompatible lifetime on type } +trait C {} +impl C for Box {} +pub trait D { + type T<'a>: C; +} +impl D for () { + // `'a` doesn't match explicit `'static`: we *should* suggest removing `'static` + type T<'a> = Box; //~ incompatible lifetime on type +} + +trait E {} +impl E for (Box, Box) {} +pub trait F { + type T<'a>: E; +} +impl F for () { + // `'a` doesn't match explicit `'static`: suggest `'_` + type T<'a> = (Box, Box); //~ incompatible lifetime on type +} + fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.stderr b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.stderr index b77b58284eefb..fedc6a341cad3 100644 --- a/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.stderr +++ b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.stderr @@ -1,5 +1,5 @@ error: incompatible lifetime on type - --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:16:5 + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:17:5 | LL | type T<'a> = Box; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,20 +9,79 @@ note: because this has an unmet lifetime requirement | LL | type T<'a>: A; | ^ introduces a `'static` lifetime requirement -note: ...the lifetime `'a` as defined on the associated item at 16:12... - --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:16:12 +note: the lifetime `'a` as defined on the associated item at 17:12... + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:17:12 | LL | type T<'a> = Box; | ^^ -note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` + = note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` +note: this has an implicit `'static` lifetime requirement --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:9:20 | LL | impl A for Box {} - | ^ this has an implicit `'static` lifetime requirement + | ^ help: consider relaxing the implicit `'static` requirement | LL | impl A for Box {} | ^^^^ -error: aborting due to previous error +error: incompatible lifetime on type + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:27:5 + | +LL | type T<'a> = Box; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: because this has an unmet lifetime requirement + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:23:17 + | +LL | type T<'a>: C; + | ^ introduces a `'static` lifetime requirement +note: the lifetime `'a` as defined on the associated item at 27:12... + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:27:12 + | +LL | type T<'a> = Box; + | ^^ +note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:21:1 + | +LL | impl C for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incompatible lifetime on type + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:37:5 + | +LL | type T<'a> = (Box, Box); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: because this has an unmet lifetime requirement + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:33:17 + | +LL | type T<'a>: E; + | ^ introduces a `'static` lifetime requirement +note: the lifetime `'a` as defined on the associated item at 37:12... + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:37:12 + | +LL | type T<'a> = (Box, Box); + | ^^ + = note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` +note: this has an implicit `'static` lifetime requirement + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:31:21 + | +LL | impl E for (Box, Box) {} + | ^ +note: this has an implicit `'static` lifetime requirement + --> $DIR/issue-78113-lifetime-mismatch-dyn-trait-box.rs:31:33 + | +LL | impl E for (Box, Box) {} + | ^ +help: consider relaxing the implicit `'static` requirement + | +LL | impl E for (Box, Box) {} + | ^^^^ +help: consider relaxing the implicit `'static` requirement + | +LL | impl E for (Box, Box) {} + | ^^^^ + +error: aborting due to 3 previous errors diff --git a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr index 6500a2a55f667..1c81c69620165 100644 --- a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr +++ b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr @@ -3,6 +3,8 @@ error[E0726]: implicit elided lifetime not allowed here | LL | impl MyTrait for Foo { | ^^^- help: indicate the anonymous lifetime: `<'_>` + | + = note: assuming a `'static` lifetime... error: aborting due to previous error diff --git a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr index ad97cb0abd623..735f01379f09f 100644 --- a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr +++ b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr @@ -3,6 +3,8 @@ error[E0726]: implicit elided lifetime not allowed here | LL | impl MyTrait for u32 { | ^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + | + = note: assuming a `'static` lifetime... error: aborting due to previous error diff --git a/src/test/ui/issues/issue-10412.stderr b/src/test/ui/issues/issue-10412.stderr index 2a6e2414593aa..9d181eab7fbcd 100644 --- a/src/test/ui/issues/issue-10412.stderr +++ b/src/test/ui/issues/issue-10412.stderr @@ -45,6 +45,8 @@ error[E0726]: implicit elided lifetime not allowed here | LL | impl<'self> Serializable for &'self str { | ^^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Serializable<'_, str>` + | + = note: assuming a `'static` lifetime... error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/issue-10412.rs:6:13 diff --git a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr index 48c9f362fe224..4e927cd983d0d 100644 --- a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr +++ b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr @@ -3,6 +3,8 @@ error[E0726]: implicit elided lifetime not allowed here | LL | impl Trait for Ref {} | ^^^- help: indicate the anonymous lifetime: `<'_>` + | + = note: assuming a `'static` lifetime... error: incompatible lifetime on type --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21 @@ -15,11 +17,16 @@ note: because this has an unmet lifetime requirement | LL | pub struct Wrapper(T); | ^^^^^ introduces a `'static` lifetime requirement -note: ...the anonymous lifetime #1 defined on the method body at 16:5... +note: the anonymous lifetime #1 defined on the method body at 16:5... --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:5 | LL | pub fn repro(_: Wrapper); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` + --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:13:1 + | +LL | impl Trait for Ref {} + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors