diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl index d28608cf47d..7a10f4b7bc5 100644 --- a/compiler/rustc_error_messages/locales/en-US/infer.ftl +++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl @@ -221,3 +221,55 @@ infer_trait_impl_diff = `impl` item signature doesn't match `trait` item signatu infer_tid_rel_help = verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output infer_tid_consider_borriwing = consider borrowing this type parameter in the trait infer_tid_param_help = the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + +infer_dtcs_has_lifetime_req_label = this has an implicit `'static` lifetime requirement +infer_dtcs_introduces_requirement = calling this method introduces the `impl`'s 'static` requirement +infer_dtcs_has_req_note = the used `impl` has a `'static` requirement +infer_dtcs_suggestion = consider relaxing the implicit `'static` requirement + +infer_but_calling_introduces = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$lifetime_kind -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` +} but calling `{assoc_item}` introduces an implicit `'static` lifetime requirement + .label1 = {$has_lifetime -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` + } + .label2 = ...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> + [named] `impl` of `{$impl_path}` + *[anon] inherent `impl` + } + +infer_but_needs_to_satisfy = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` +} but it needs to satisfy a `'static` lifetime requirement + .influencer = this data with {$has_lifetime -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` + }... + .require = {$spans_empty -> + *[true] ...is used and required to live as long as `'static` here + [false] ...and is required to live as long as `'static` here + } + .used_here = ...is used here... + .introduced_by_bound = 'static` lifetime requirement introduced by this bound + +infer_more_targeted = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` +} but calling `{$ident}` introduces an implicit `'static` lifetime requirement + +infer_ril_introduced_here = `'static` requirement introduced here +infer_ril_introduced_by = requirement introduced by this return type +infer_ril_because_of = because of this returned expression +infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index e2579ab7b7c..dc79c725951 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -8,6 +8,7 @@ use rustc_hir::{FnRetTy, Ty}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{Region, TyCtxt}; use rustc_span::symbol::kw; +use rustc_span::Symbol; use rustc_span::{symbol::Ident, BytePos, Span}; use crate::infer::error_reporting::{ @@ -619,3 +620,99 @@ pub struct TraitImplDiff { pub expected: String, pub found: String, } + +pub struct DynTraitConstraintSuggestion { + pub span: Span, + pub ident: Ident, +} + +impl AddSubdiagnostic for DynTraitConstraintSuggestion { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + let mut multi_span: MultiSpan = vec![self.span].into(); + multi_span.push_span_label(self.span, fluent::infer::dtcs_has_lifetime_req_label); + multi_span.push_span_label(self.ident.span, fluent::infer::dtcs_introduces_requirement); + diag.span_note(multi_span, fluent::infer::dtcs_has_req_note); + diag.span_suggestion_verbose( + self.span.shrink_to_hi(), + fluent::infer::dtcs_suggestion, + " + '_", + Applicability::MaybeIncorrect, + ); + } +} + +#[derive(SessionDiagnostic)] +#[diag(infer::but_calling_introduces, code = "E0772")] +pub struct ButCallingIntroduces { + #[label(infer::label1)] + pub param_ty_span: Span, + #[primary_span] + #[label(infer::label2)] + pub cause_span: Span, + + pub has_param_name: bool, + pub param_name: String, + pub has_lifetime: bool, + pub lifetime: String, + pub assoc_item: Symbol, + pub has_impl_path: bool, + pub impl_path: String, +} + +pub struct ReqIntroducedLocations { + pub span: MultiSpan, + pub spans: Vec, + pub fn_decl_span: Span, + pub cause_span: Span, + pub add_label: bool, +} + +impl AddSubdiagnostic for ReqIntroducedLocations { + fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) { + for sp in self.spans { + self.span.push_span_label(sp, fluent::infer::ril_introduced_here); + } + + if self.add_label { + self.span.push_span_label(self.fn_decl_span, fluent::infer::ril_introduced_by); + } + self.span.push_span_label(self.cause_span, fluent::infer::ril_because_of); + diag.span_note(self.span, fluent::infer::ril_static_introduced_by); + } +} + +pub struct MoreTargeted { + pub ident: Symbol, +} + +impl AddSubdiagnostic for MoreTargeted { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + diag.code(rustc_errors::error_code!(E0772)); + diag.set_primary_message(fluent::infer::more_targeted); + diag.set_arg("ident", self.ident); + } +} + +#[derive(SessionDiagnostic)] +#[diag(infer::but_needs_to_satisfy, code = "E0759")] +pub struct ButNeedsToSatisfy { + #[primary_span] + pub sp: Span, + #[label(infer::influencer)] + pub influencer_point: Span, + #[label(infer::used_here)] + pub spans: Vec, + #[label(infer::require)] + pub require_span_as_label: Option, + #[note(infer::require)] + pub require_span_as_note: Option, + #[note(infer::introduced_by_bound)] + pub bound: Option, + + #[subdiagnostic] + pub req_introduces_loc: Option, + + pub spans_empty: bool, + pub has_lifetime: bool, + pub lifetime: String, +} 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 9bd2202d260..b76f7e7689f 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 @@ -1,11 +1,15 @@ //! Error Reporting for static impl Traits. +use crate::errors::{ + ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted, + ReqIntroducedLocations, +}; 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::fx::FxIndexSet; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{AddSubdiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; @@ -49,46 +53,33 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } let param = self.find_param_with_region(*sup_r, *sub_r)?; - let lifetime = if sup_r.has_name() { - format!("lifetime `{}`", sup_r) - } else { - "an anonymous lifetime `'_`".to_string() + let simple_ident = param.param.pat.simple_ident(); + + let (has_impl_path, impl_path) = match ctxt.assoc_item.container { + AssocItemContainer::TraitContainer => { + let id = ctxt.assoc_item.container_id(tcx); + (true, tcx.def_path_str(id)) + } + AssocItemContainer::ImplContainer => (false, String::new()), }; - let mut err = struct_span_err!( - tcx.sess, - cause.span, - E0772, - "{} has {} but calling `{}` introduces an implicit `'static` lifetime \ - requirement", - param - .param - .pat - .simple_ident() - .map(|s| format!("`{}`", s)) - .unwrap_or_else(|| "`fn` parameter".to_string()), - lifetime, - ctxt.assoc_item.name, - ); - err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); - err.span_label( - cause.span, - &format!( - "...is used and required to live as long as `'static` here \ - because of an implicit lifetime bound on the {}", - match ctxt.assoc_item.container { - AssocItemContainer::TraitContainer => { - let id = ctxt.assoc_item.container_id(tcx); - format!("`impl` of `{}`", tcx.def_path_str(id)) - } - AssocItemContainer::ImplContainer => "inherent `impl`".to_string(), - }, - ), - ); + + let diag = ButCallingIntroduces { + param_ty_span: param.param_ty_span, + cause_span: cause.span, + has_param_name: simple_ident.is_some(), + param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), + has_lifetime: sup_r.has_name(), + lifetime: sup_r.to_string(), + assoc_item: ctxt.assoc_item.name, + has_impl_path, + impl_path, + }; + let mut err = self.tcx().sess.create_err(diag); if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) { let reported = err.emit(); return Some(reported); } else { - err.cancel(); + err.cancel() } } return None; @@ -104,25 +95,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sp = var_origin.span(); let return_sp = sub_origin.span(); let param = self.find_param_with_region(*sup_r, *sub_r)?; - let (lifetime_name, lifetime) = if sup_r.has_name() { - (sup_r.to_string(), format!("lifetime `{}`", sup_r)) - } else { - ("'_".to_owned(), "an anonymous lifetime `'_`".to_string()) - }; - let param_name = param - .param - .pat - .simple_ident() - .map(|s| format!("`{}`", s)) - .unwrap_or_else(|| "`fn` parameter".to_string()); - let mut err = struct_span_err!( - tcx.sess, - sp, - E0759, - "{} has {} but it needs to satisfy a `'static` lifetime requirement", - param_name, - lifetime, - ); + let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; let (mention_influencer, influencer_point) = if sup_origin.span().overlaps(param.param_ty_span) { @@ -141,7 +114,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } else { (!sup_origin.span().overlaps(return_sp), param.param_ty_span) }; - err.span_label(influencer_point, &format!("this data with {}...", lifetime)); debug!("try_report_static_impl_trait: param_info={:?}", param); @@ -155,31 +127,19 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { spans.dedup_by_key(|span| (span.lo(), span.hi())); // We try to make the output have fewer overlapping spans if possible. - let require_msg = if spans.is_empty() { - "...is used and required to live as long as `'static` here" - } else { - "...and is required to live as long as `'static` here" - }; let require_span = if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp }; - for span in &spans { - err.span_label(*span, "...is used here..."); - } - - if spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp) { - // If any of the "captured here" labels appears on the same line or after - // `require_span`, we put it on a note to ensure the text flows by appearing - // always at the end. - err.span_note(require_span, require_msg); + let spans_empty = spans.is_empty(); + let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp); + let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { + Some(*bound) } else { - // We don't need a note, it's already at the end, it can be shown as a `span_label`. - err.span_label(require_span, require_msg); - } + None + }; + + let mut subdiag = None; - if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { - err.span_note(*bound, "`'static` lifetime requirement introduced by this bound"); - } if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin { if let ObligationCauseCode::ReturnValue(hir_id) | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code() @@ -187,33 +147,50 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let parent_id = tcx.hir().get_parent_item(*hir_id); if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) { let mut span: MultiSpan = fn_decl.output.span().into(); + let mut spans = Vec::new(); let mut add_label = true; if let hir::FnRetTy::Return(ty) = fn_decl.output { let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); v.visit_ty(ty); if !v.0.is_empty() { span = v.0.clone().into(); - for sp in v.0 { - span.push_span_label(sp, "`'static` requirement introduced here"); - } + spans = v.0; add_label = false; } } - if add_label { - span.push_span_label( - fn_decl.output.span(), - "requirement introduced by this return type", - ); - } - span.push_span_label(cause.span, "because of this returned expression"); - err.span_note( + let fn_decl_span = fn_decl.output.span(); + + subdiag = Some(ReqIntroducedLocations { span, - "`'static` lifetime requirement introduced by the return type", - ); + spans, + fn_decl_span, + cause_span: cause.span, + add_label, + }); } } } + let diag = ButNeedsToSatisfy { + sp, + influencer_point, + spans: spans.clone(), + // If any of the "captured here" labels appears on the same line or after + // `require_span`, we put it on a note to ensure the text flows by appearing + // always at the end. + require_span_as_note: require_as_note.then_some(require_span), + // We don't need a note, it's already at the end, it can be shown as a `span_label`. + require_span_as_label: (!require_as_note).then_some(require_span), + req_introduces_loc: subdiag, + + has_lifetime: sup_r.has_name(), + lifetime: sup_r.to_string(), + spans_empty, + bound, + }; + + let mut err = self.tcx().sess.create_err(diag); + let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); let mut override_error_code = None; @@ -247,12 +224,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) { // Provide a more targeted error code and description. - err.code(rustc_errors::error_code!(E0772)); - err.set_primary_message(&format!( - "{} has {} but calling `{}` introduces an implicit `'static` lifetime \ - requirement", - param_name, lifetime, ident, - )); + let retarget_subdiag = MoreTargeted { ident }; + retarget_subdiag.add_to_diagnostic(&mut err); } let arg = match param.param.pat.simple_ident() { @@ -513,21 +486,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); hir_v.visit_ty(&self_ty); - for span in &traits { - let mut multi_span: MultiSpan = vec![*span].into(); - multi_span - .push_span_label(*span, "this has an implicit `'static` lifetime requirement"); - multi_span.push_span_label( - ident.span, - "calling this method introduces the `impl`'s 'static` requirement", - ); - err.span_note(multi_span, "the used `impl` has a `'static` requirement"); - err.span_suggestion_verbose( - span.shrink_to_hi(), - "consider relaxing the implicit `'static` requirement", - " + '_", - Applicability::MaybeIncorrect, - ); + for &span in &traits { + let subdiag = DynTraitConstraintSuggestion { span, ident }; + subdiag.add_to_diagnostic(err); suggested = true; } }