//! Error reporting machinery for lifetime errors.
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_infer::infer::{
- error_reporting::nice_region_error::NiceRegionError,
- error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin,
+ error_reporting::nice_region_error::{
+ self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
+ NiceRegionError,
+ },
+ error_reporting::unexpected_hidden_region_diagnostic,
+ NllRegionVariableOrigin, RelateParamBound,
};
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
-use rustc_middle::ty::subst::{InternalSubsts, Subst};
+use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{self, RegionVid, Ty};
-use rustc_span::symbol::{kw, sym};
-use rustc_span::{BytePos, Span};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
use crate::borrowck_errors;
let type_test_span = type_test.locations.span(&self.body);
if let Some(lower_bound_region) = lower_bound_region {
+ let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx);
+ let origin = RelateParamBound(type_test_span, generic_ty, None);
self.buffer_error(self.infcx.construct_generic_bound_failure(
type_test_span,
- None,
+ Some(origin),
type_test.generic_kind,
lower_bound_region,
+ self.body.source.def_id().as_local(),
));
} else {
// FIXME. We should handle this case better. It
/// Report an error because the universal region `fr` was required to outlive
/// `outlived_fr` but it is not known to do so. For example:
///
- /// ```
+ /// ```compile_fail,E0312
/// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
/// ```
///
ty::RawPtr(ty_mut) => {
assert_eq!(ty_mut.mutbl, rustc_hir::Mutability::Mut);
(
- format!("a mutable pointer to {}", ty_mut.ty),
+ 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);
(
- format!("a mutable reference to {}", inner_ty),
+ format!("a mutable reference to `{inner_ty}`"),
"mutable references are invariant over their type parameter"
.to_string(),
)
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)
+ }
+ ty::FnDef(def_id, _) => {
+ let name = self.infcx.tcx.item_name(*def_id);
+ let identity_substs =
+ InternalSubsts::identity_for_item(self.infcx.tcx, *def_id);
+ let desc = format!("a function pointer to `{name}`");
+ let note = format!(
+ "the function `{name}` is invariant over the parameter `{}`",
+ identity_substs[param_index as usize]
);
(desc, note)
}
if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
diag.span_label(
outlived_fr_span,
- format!(
- "`{}` declared here, outside of the {} body",
- outlived_fr_name, escapes_from
- ),
+ format!("`{outlived_fr_name}` declared here, outside of the {escapes_from} body",),
);
}
diag.span_label(
fr_span,
format!(
- "`{}` is a reference that is only valid in the {} body",
- fr_name, escapes_from
+ "`{fr_name}` is a reference that is only valid in the {escapes_from} body",
),
);
- diag.span_label(*span, format!("`{}` escapes the {} body here", fr_name, escapes_from));
+ diag.span_label(*span, format!("`{fr_name}` escapes the {escapes_from} body here"));
}
// Only show an extra note if we can find an 'error region' for both of the region
diag.span_label(
*span,
format!(
- "{} was supposed to return data with lifetime `{}` but it is returning \
- data with lifetime `{}`",
- mir_def_name, outlived_fr_name, fr_name
+ "{mir_def_name} was supposed to return data with lifetime `{outlived_fr_name}` but it is returning \
+ data with lifetime `{fr_name}`",
),
);
}
}
self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
+ self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
diag
}
fr_name: RegionName,
outlived_fr: RegionVid,
) {
- if let (Some(f), Some(ty::ReStatic)) =
- (self.to_error_region(fr), self.to_error_region(outlived_fr).as_deref())
+ if let (Some(f), Some(outlived_f)) =
+ (self.to_error_region(fr), self.to_error_region(outlived_fr))
{
- if let Some(&ty::Opaque(did, substs)) = self
+ if *outlived_f != ty::ReStatic {
+ return;
+ }
+
+ let fn_returns = self
.infcx
.tcx
.is_suitable_region(f)
- .map(|r| r.def_id)
- .and_then(|id| self.infcx.tcx.return_type_impl_trait(id))
- .map(|(ty, _)| ty.kind())
- {
- // Check whether or not the impl trait return type is intended to capture
- // data with the static lifetime.
- //
- // eg. check for `impl Trait + 'static` instead of `impl Trait`.
- let has_static_predicate = {
- let bounds = self.infcx.tcx.explicit_item_bounds(did);
-
- let mut found = false;
- for (bound, _) in bounds {
- if let ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, r)) =
- bound.kind().skip_binder()
- {
- let r = r.subst(self.infcx.tcx, substs);
- if r.is_static() {
- found = true;
- break;
- } else {
- // If there's already a lifetime bound, don't
- // suggest anything.
- return;
- }
- }
- }
-
- found
- };
+ .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id))
+ .unwrap_or_default();
- debug!(
- "add_static_impl_trait_suggestion: has_static_predicate={:?}",
- has_static_predicate
- );
- let static_str = kw::StaticLifetime;
- // If there is a static predicate, then the only sensible suggestion is to replace
- // fr with `'static`.
- if has_static_predicate {
- diag.help(&format!("consider replacing `{}` with `{}`", fr_name, static_str));
- } else {
- // Otherwise, we should suggest adding a constraint on the return type.
- let span = self.infcx.tcx.def_span(did);
- if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
- let suggestable_fr_name = if fr_name.was_named() {
- fr_name.to_string()
- } else {
- "'_".to_string()
- };
- let span = if snippet.ends_with(';') {
- // `type X = impl Trait;`
- span.with_hi(span.hi() - BytePos(1))
- } else {
- span
- };
- let suggestion = format!(" + {}", suggestable_fr_name);
- let span = span.shrink_to_hi();
- diag.span_suggestion(
- span,
- &format!(
- "to allow this `impl Trait` to capture borrowed data with lifetime \
- `{}`, add `{}` as a bound",
- fr_name, suggestable_fr_name,
- ),
- suggestion,
- Applicability::MachineApplicable,
- );
- }
- }
+ if fn_returns.is_empty() {
+ return;
}
+
+ let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
+ param
+ } else {
+ return;
+ };
+
+ let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() };
+
+ let arg = match param.param.pat.simple_ident() {
+ Some(simple_ident) => format!("argument `{}`", simple_ident),
+ None => "the argument".to_string(),
+ };
+ let captures = format!("captures data from {}", arg);
+
+ return nice_region_error::suggest_new_region_bound(
+ self.infcx.tcx,
+ diag,
+ fn_returns,
+ lifetime,
+ Some(arg),
+ captures,
+ Some((param.param_ty_span, param.param_ty.to_string())),
+ );
}
}
+
+ fn suggest_adding_lifetime_params(
+ &self,
+ diag: &mut Diagnostic,
+ sub: RegionVid,
+ sup: RegionVid,
+ ) {
+ let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else {
+ return
+ };
+
+ let Some((ty_sub, _)) = self
+ .infcx
+ .tcx
+ .is_suitable_region(sub)
+ .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) else {
+ return
+ };
+
+ let Some((ty_sup, _)) = self
+ .infcx
+ .tcx
+ .is_suitable_region(sup)
+ .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) else {
+ return
+ };
+
+ suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
+ }
}