about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-03-07 15:44:07 +0000
committerMichael Goulet <michael@errs.io>2024-03-08 19:08:13 +0000
commitffd30e0a6939d7d25c4ac28bfac4b4e367adcd59 (patch)
tree079aa5d6de0958fa94d642c53a3c8077baf65153 /compiler
parent74acabe9b042ea8c42862ee29aca2a8b7d333644 (diff)
downloadrust-ffd30e0a6939d7d25c4ac28bfac4b4e367adcd59.tar.gz
rust-ffd30e0a6939d7d25c4ac28bfac4b4e367adcd59.zip
Improve error message for opaque captures
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0657.md59
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs68
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs12
4 files changed, 70 insertions, 73 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0657.md b/compiler/rustc_error_codes/src/error_codes/E0657.md
index 7fe48c51147..477d8e8bb6d 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0657.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0657.md
@@ -1,57 +1,26 @@
-A lifetime bound on a trait implementation was captured at an incorrect place.
+An `impl Trait` captured a higher-ranked lifetime, which is not supported.
+
+Currently, `impl Trait` types are only allowed to capture lifetimes from
+their parent items, and not from any `for<'a>` binders in scope.
 
 Erroneous code example:
 
 ```compile_fail,E0657
-trait Id<T> {}
-trait Lt<'a> {}
-
-impl<'a> Lt<'a> for () {}
-impl<T> Id<T> for T {}
-
-fn free_fn_capture_hrtb_in_impl_trait()
-    -> Box<for<'a> Id<impl Lt<'a>>> // error!
-{
-    Box::new(())
-}
+trait BorrowInto<'a> {
+    type Target;
 
-struct Foo;
-impl Foo {
-    fn impl_fn_capture_hrtb_in_impl_trait()
-        -> Box<for<'a> Id<impl Lt<'a>>> // error!
-    {
-        Box::new(())
-    }
+    fn borrow_into(&'a self) -> Self::Target;
 }
-```
 
-Here, you have used the inappropriate lifetime in the `impl Trait`,
-The `impl Trait` can only capture lifetimes bound at the fn or impl
-level.
+impl<'a> BorrowInto<'a> for () {
+    type Target = &'a ();
 
-To fix this we have to define the lifetime at the function or impl
-level and use that lifetime in the `impl Trait`. For example you can
-define the lifetime at the function:
-
-```
-trait Id<T> {}
-trait Lt<'a> {}
-
-impl<'a> Lt<'a> for () {}
-impl<T> Id<T> for T {}
-
-fn free_fn_capture_hrtb_in_impl_trait<'b>()
-    -> Box<for<'a> Id<impl Lt<'b>>> // ok!
-{
-    Box::new(())
+    fn borrow_into(&'a self) -> Self::Target {
+        self
+    }
 }
 
-struct Foo;
-impl Foo {
-    fn impl_fn_capture_hrtb_in_impl_trait<'b>()
-        -> Box<for<'a> Id<impl Lt<'b>>> // ok!
-    {
-        Box::new(())
-    }
+fn opaque() -> impl for<'a> BorrowInto<'a, Target = impl Sized + 'a> {
+    ()
 }
 ```
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index e376411cd95..bad472dcb5c 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -312,6 +312,10 @@ hir_analysis_only_current_traits_primitive = only traits defined in the current
 
 hir_analysis_only_current_traits_ty = `{$ty}` is not defined in the current crate
 
+hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot capture {$bad_place}
+    .label = `impl Trait` implicitly captures all lifetimes in scope
+    .note = lifetime declared here
+
 hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
     .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
 
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 607ada8b5ed..6531cec7650 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -8,7 +8,6 @@
 
 use rustc_ast::visit::walk_list;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
-use rustc_errors::{codes::*, struct_span_code_err};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::LocalDefId;
@@ -673,34 +672,47 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     // and ban them. Type variables instantiated inside binders aren't
                     // well-supported at the moment, so this doesn't work.
                     // In the future, this should be fixed and this error should be removed.
-                    let def = self.map.defs.get(&lifetime.hir_id).cloned();
-                    let Some(ResolvedArg::LateBound(_, _, def_id)) = def else { continue };
-                    let Some(def_id) = def_id.as_local() else { continue };
-                    let hir_id = self.tcx.local_def_id_to_hir_id(def_id);
-                    // Ensure that the parent of the def is an item, not HRTB
-                    let parent_id = self.tcx.parent_hir_id(hir_id);
-                    if !parent_id.is_owner() {
-                        struct_span_code_err!(
-                            self.tcx.dcx(),
-                            lifetime.ident.span,
-                            E0657,
-                            "`impl Trait` can only capture lifetimes bound at the fn or impl level"
-                        )
-                        .emit();
-                        self.uninsert_lifetime_on_error(lifetime, def.unwrap());
-                    }
-                    if let hir::Node::Item(hir::Item {
-                        kind: hir::ItemKind::OpaqueTy { .. }, ..
-                    }) = self.tcx.hir_node(parent_id)
+                    let def = self.map.defs.get(&lifetime.hir_id).copied();
+                    let Some(ResolvedArg::LateBound(_, _, lifetime_def_id)) = def else { continue };
+                    let Some(lifetime_def_id) = lifetime_def_id.as_local() else { continue };
+                    let lifetime_hir_id = self.tcx.local_def_id_to_hir_id(lifetime_def_id);
+
+                    let bad_place = match self.tcx.hir_node(self.tcx.parent_hir_id(lifetime_hir_id))
                     {
-                        self.tcx.dcx().struct_span_err(
-                            lifetime.ident.span,
-                            "higher kinded lifetime bounds on nested opaque types are not supported yet",
-                        )
-                        .with_span_note(self.tcx.def_span(def_id), "lifetime declared here")
-                        .emit();
-                        self.uninsert_lifetime_on_error(lifetime, def.unwrap());
-                    }
+                        // Opaques do not declare their own lifetimes, so if a lifetime comes from an opaque
+                        // it must be a reified late-bound lifetime from a trait goal.
+                        hir::Node::Item(hir::Item {
+                            kind: hir::ItemKind::OpaqueTy { .. }, ..
+                        }) => "higher-ranked lifetime from outer `impl Trait`",
+                        // Other items are fine.
+                        hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => {
+                            continue;
+                        }
+                        hir::Node::Ty(hir::Ty { kind: hir::TyKind::BareFn(_), .. }) => {
+                            "higher-ranked lifetime from function pointer"
+                        }
+                        hir::Node::Ty(hir::Ty { kind: hir::TyKind::TraitObject(..), .. }) => {
+                            "higher-ranked lifetime from `dyn` type"
+                        }
+                        _ => "higher-ranked lifetime",
+                    };
+
+                    let (span, label) = if lifetime.ident.span == self.tcx.def_span(lifetime_def_id)
+                    {
+                        let opaque_span = self.tcx.def_span(item_id.owner_id);
+                        (opaque_span, Some(opaque_span))
+                    } else {
+                        (lifetime.ident.span, None)
+                    };
+
+                    // Ensure that the parent of the def is an item, not HRTB
+                    self.tcx.dcx().emit_err(errors::OpaqueCapturesHigherRankedLifetime {
+                        span,
+                        label,
+                        decl_span: self.tcx.def_span(lifetime_def_id),
+                        bad_place,
+                    });
+                    self.uninsert_lifetime_on_error(lifetime, def.unwrap());
                 }
             }
             _ => intravisit::walk_ty(self, ty),
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 5330260fbf5..26349cd1c65 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1607,3 +1607,15 @@ pub struct UnnamedFieldsReprFieldDefined {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_opaque_captures_higher_ranked_lifetime, code = E0657)]
+pub struct OpaqueCapturesHigherRankedLifetime {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub label: Option<Span>,
+    #[note]
+    pub decl_span: Span,
+    pub bad_place: &'static str,
+}