]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_borrowck/src/diagnostics/region_errors.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / compiler / rustc_borrowck / src / diagnostics / region_errors.rs
index e63450a1f58aa02b3172bcd02025a019a7d49cc7..6f1c8daf42e3f72905546673aa9ecd16b1be5388 100644 (file)
@@ -1,16 +1,20 @@
 //! 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;
 
@@ -166,11 +170,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     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
@@ -252,7 +259,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     /// 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 }
     /// ```
     ///
@@ -330,14 +337,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     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(),
                         )
@@ -351,10 +358,21 @@ 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)
+                    }
+                    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)
                     }
@@ -512,10 +530,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         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",),
             );
         }
 
@@ -523,12 +538,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             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
@@ -600,9 +614,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 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}`",
                     ),
                 );
             }
@@ -620,6 +633,7 @@ 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);
 
         diag
     }
@@ -641,83 +655,76 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         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);
+    }
 }