about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNikita Tomashevich <quant3234@gmail.com>2022-08-30 18:28:50 +0300
committerNikita Tomashevich <quant3234@gmail.com>2022-09-06 18:41:08 +0300
commite0e9b21c78e4e6a7a5515944ace4cb0c1f2f9253 (patch)
treeb5c48ad2c67b6c4ee0078a6985190b0a3be17892
parentaf3343ae299c81a019d9d62d15c10cb99d7ceb89 (diff)
downloadrust-e0e9b21c78e4e6a7a5515944ace4cb0c1f2f9253.tar.gz
rust-e0e9b21c78e4e6a7a5515944ace4cb0c1f2f9253.zip
Mugrate mismatched_static_lifetime.rs
-rw-r--r--compiler/rustc_error_messages/locales/en-US/infer.ftl31
-rw-r--r--compiler/rustc_infer/src/errors/mod.rs (renamed from compiler/rustc_infer/src/errors.rs)68
-rw-r--r--compiler/rustc_infer/src/errors/note_and_explain.rs176
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs48
4 files changed, 303 insertions, 20 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl
index 478a4bdf8a9..2899b8304bc 100644
--- a/compiler/rustc_error_messages/locales/en-US/infer.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl
@@ -137,3 +137,34 @@ infer_lifetime_param_suggestion = consider introducing a named lifetime paramete
     *[false] {""}
 }
 infer_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime
+
+infer_region_explanation = {$pref_kind ->
+    *[should_not_happen] [{$pref_kind}]
+    [empty] {""}
+}{$pref_kind ->
+    [empty] {""}
+    *[other] {" "}
+}{$desc_kind ->
+    *[should_not_happen] [{$desc_kind}]
+    [restatic] the static lifetime
+    [reempty] the empty lifetime
+    [reemptyuni] the empty lifetime in universe {$desc_arg}
+    [revar] lifetime {$desc_arg}
+
+    [as_defined] the lifetime `{$desc_arg}` as defined here
+    [as_defined_anon] the anonymous lifetime as defined here
+    [defined_here] the anonymous lifetime defined here
+    [anon_num_here] the anonymous lifetime #{$desc_num_arg} defined here
+    [defined_here_reg] the lifetime `{$desc_arg}` as defined here
+}{$suff_kind ->
+    *[should_not_happen] [{$suff_kind}]
+    [empty]{""}
+    [continues] ...
+}
+
+infer_mismatched_static_lifetime = incompatible lifetime on type
+infer_msl_impl_note = ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
+infer_msl_introduces_static = introduces a `'static` lifetime requirement
+infer_msl_unmet_req = because this has an unmet lifetime requirement
+infer_msl_trait_note = this has an implicit `'static` lifetime requirement
+infer_msl_trait_sugg = consider relaxing the implicit `'static` requirement
diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors/mod.rs
index 932ba1f35af..ad8eb2945fa 100644
--- a/compiler/rustc_infer/src/errors.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -14,6 +14,8 @@ use crate::infer::error_reporting::{
     ObligationCauseAsDiagArg,
 };
 
+pub mod note_and_explain;
+
 #[derive(SessionDiagnostic)]
 #[diag(infer::opaque_hidden_type)]
 pub struct OpaqueHiddenTypeDiag {
@@ -419,3 +421,69 @@ pub struct LifetimeMismatch<'a> {
     #[subdiagnostic]
     pub suggestion: AddLifetimeParamsSuggestion<'a>,
 }
+
+pub mod mismatched_static_lifetime {
+    use rustc_errors::{self, fluent, AddSubdiagnostic, MultiSpan};
+    use rustc_span::Span;
+
+    use super::note_and_explain;
+
+    pub struct LabeledMultiSpan {
+        pub multi_span: MultiSpan,
+        pub binding_span: Span,
+    }
+
+    impl AddSubdiagnostic for LabeledMultiSpan {
+        fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) {
+            self.multi_span
+                .push_span_label(self.binding_span, fluent::infer::msl_introduces_static);
+            diag.span_note(self.multi_span, fluent::infer::msl_unmet_req);
+        }
+    }
+
+    pub struct ImplNote {
+        pub impl_span: Option<Span>,
+    }
+
+    impl AddSubdiagnostic for ImplNote {
+        fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+            match self.impl_span {
+                Some(span) => diag.span_note(span, fluent::infer::msl_impl_note),
+                None => diag.note(fluent::infer::msl_impl_note),
+            };
+        }
+    }
+
+    #[derive(SessionSubdiagnostic)]
+    pub enum TraitSubdiag {
+        #[note(infer::msl_trait_note)]
+        Note {
+            #[primary_span]
+            span: Span,
+        },
+        #[suggestion_verbose(
+            infer::msl_trait_sugg,
+            code = " + '_",
+            applicability = "maybe-incorrect"
+        )]
+        Sugg {
+            #[primary_span]
+            span: Span,
+        },
+    }
+
+    #[derive(SessionDiagnostic)]
+    #[diag(infer::mismatched_static_lifetime)]
+    pub struct MismatchedStaticLifetime<'a> {
+        #[primary_span]
+        pub cause_span: Span,
+        #[subdiagnostic]
+        pub multispan_subdiag: LabeledMultiSpan,
+        #[subdiagnostic]
+        pub expl: Option<note_and_explain::RegionExplanation<'a>>,
+        #[subdiagnostic]
+        pub impl_note: ImplNote,
+        #[subdiagnostic]
+        pub trait_subdiags: Vec<TraitSubdiag>,
+    }
+}
diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs
new file mode 100644
index 00000000000..92bf3ecd131
--- /dev/null
+++ b/compiler/rustc_infer/src/errors/note_and_explain.rs
@@ -0,0 +1,176 @@
+use crate::infer::error_reporting::nice_region_error::find_anon_type;
+use rustc_errors::{self, fluent, AddSubdiagnostic};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::{symbol::kw, Span};
+
+#[derive(Default)]
+struct DescriptionCtx<'a> {
+    span: Option<Span>,
+    kind: &'a str,
+    arg: String,
+    num_arg: u32,
+}
+
+impl<'a> DescriptionCtx<'a> {
+    fn new<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        region: ty::Region<'tcx>,
+        alt_span: Option<Span>,
+    ) -> Option<Self> {
+        let mut me = DescriptionCtx::default();
+        me.span = alt_span;
+        match *region {
+            ty::ReEarlyBound(_) | ty::ReFree(_) => {
+                return Self::from_early_bound_and_free_regions(tcx, region);
+            }
+            ty::ReStatic => {
+                me.kind = "restatic";
+            }
+
+            ty::ReEmpty(ty::UniverseIndex::ROOT) => me.kind = "reempty",
+
+            // uh oh, hope no user ever sees THIS
+            ty::ReEmpty(ui) => {
+                me.kind = "reemptyuni";
+                me.arg = format!("{:?}", ui);
+            }
+
+            ty::RePlaceholder(_) => return None,
+
+            // FIXME(#13998) RePlaceholder should probably print like
+            // ReFree rather than dumping Debug output on the user.
+            //
+            // We shouldn't really be having unification failures with ReVar
+            // and ReLateBound though.
+            ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
+                me.kind = "revar";
+                me.arg = format!("{:?}", region);
+            }
+        };
+        Some(me)
+    }
+
+    fn from_early_bound_and_free_regions<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        region: ty::Region<'tcx>,
+    ) -> Option<Self> {
+        let mut me = DescriptionCtx::default();
+        let scope = region.free_region_binding_scope(tcx).expect_local();
+        match *region {
+            ty::ReEarlyBound(ref br) => {
+                let mut sp = tcx.def_span(scope);
+                if let Some(param) =
+                    tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
+                {
+                    sp = param.span;
+                }
+                if br.has_name() {
+                    me.kind = "as_defined";
+                    me.arg = br.name.to_string();
+                } else {
+                    me.kind = "as_defined_anon";
+                };
+                me.span = Some(sp)
+            }
+            ty::ReFree(ref fr) => {
+                if !fr.bound_region.is_named()
+                    && let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region)
+                {
+                    me.kind = "defined_here";
+                    me.span = Some(ty.span);
+                } else {
+                    match fr.bound_region {
+                        ty::BoundRegionKind::BrNamed(_, name) => {
+                            let mut sp = tcx.def_span(scope);
+                            if let Some(param) =
+                                tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
+                            {
+                                sp = param.span;
+                            }
+                            if name == kw::UnderscoreLifetime {
+                                me.kind = "as_defined_anon";
+                            } else {
+                                me.kind = "as_defined";
+                    me.arg = name.to_string();
+                            };
+                            me.span = Some(sp);
+                        }
+                        ty::BrAnon(idx) => {
+                            me.kind = "anon_num_here";
+                            me.num_arg = idx+1;
+                            me.span = Some(tcx.def_span(scope));
+                        },
+                        _ => {
+                            me.kind = "defined_here_reg";
+                            me.arg = region.to_string();
+                            me.span = Some(tcx.def_span(scope));
+                        },
+                    }
+                }
+            }
+            _ => bug!(),
+        }
+        Some(me)
+    }
+
+    fn add_to(self, diag: &mut rustc_errors::Diagnostic) {
+        diag.set_arg("desc_kind", self.kind);
+        diag.set_arg("desc_arg", self.arg);
+        diag.set_arg("desc_num_arg", self.num_arg);
+    }
+}
+
+pub enum PrefixKind {
+    Empty,
+}
+
+pub enum SuffixKind {
+    Continues,
+}
+
+impl PrefixKind {
+    fn add_to(self, diag: &mut rustc_errors::Diagnostic) {
+        match self {
+            Self::Empty => diag.set_arg("pref_kind", "empty"),
+        };
+    }
+}
+
+impl SuffixKind {
+    fn add_to(self, diag: &mut rustc_errors::Diagnostic) {
+        match self {
+            Self::Continues => diag.set_arg("suff_kind", "continues"),
+        };
+    }
+}
+
+pub struct RegionExplanation<'a> {
+    desc: DescriptionCtx<'a>,
+    prefix: PrefixKind,
+    suffix: SuffixKind,
+}
+
+impl RegionExplanation<'_> {
+    pub fn new<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        region: ty::Region<'tcx>,
+        alt_span: Option<Span>,
+        prefix: PrefixKind,
+        suffix: SuffixKind,
+    ) -> Option<Self> {
+        Some(Self { desc: DescriptionCtx::new(tcx, region, alt_span)?, prefix, suffix })
+    }
+}
+
+impl AddSubdiagnostic for RegionExplanation<'_> {
+    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+        if let Some(span) = self.desc.span {
+            diag.span_note(span, fluent::infer::region_explanation);
+        } else {
+            diag.note(fluent::infer::region_explanation);
+        }
+        self.desc.add_to(diag);
+        self.prefix.add_to(diag);
+        self.suffix.add_to(diag);
+    }
+}
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 c20b96cae2e..832a0eaba3b 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
@@ -1,13 +1,14 @@
 //! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
 //! to hold.
 
+use crate::errors::mismatched_static_lifetime::{ImplNote, MismatchedStaticLifetime, TraitSubdiag};
+use crate::errors::{mismatched_static_lifetime::LabeledMultiSpan, note_and_explain};
 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_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::ty::TypeVisitor;
@@ -39,12 +40,20 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             = *parent.code() else {
             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");
-        err.span_note(multi_span, "because this has an unmet lifetime requirement");
-        note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span));
+        let multi_span: MultiSpan = vec![binding_span].into();
+        let multispan_subdiag = LabeledMultiSpan { multi_span, binding_span };
+
+        let expl = note_and_explain::RegionExplanation::new(
+            self.tcx(),
+            sup,
+            Some(binding_span),
+            note_and_explain::PrefixKind::Empty,
+            note_and_explain::SuffixKind::Continues,
+        );
+        let mut impl_span = None;
+        let mut trait_subdiags = Vec::new();
         if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
             // If an impl is local, then maybe this isn't what they want. Try to
             // be as helpful as possible with implicit lifetimes.
@@ -73,31 +82,30 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                 // 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`");
+                impl_span = Some(self.tcx().def_span(*impl_def_id));
             } 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");
+                    trait_subdiags.push(TraitSubdiag::Note { span: *span });
                     // 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",
-                        " + '_",
-                        Applicability::MaybeIncorrect,
-                    );
+                    trait_subdiags.push(TraitSubdiag::Sugg { span: span.shrink_to_hi() });
                 }
             }
         } 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`");
+            impl_span = Some(self.tcx().def_span(*impl_def_id));
         }
-        let reported = err.emit();
+        let err = MismatchedStaticLifetime {
+            cause_span: cause.span,
+            multispan_subdiag,
+            expl,
+            impl_note: ImplNote { impl_span },
+            trait_subdiags,
+        };
+        let reported = self.tcx().sess.emit_err(err);
         Some(reported)
     }
 }