Skip to content

Commit da312fc

Browse files
Improve error message for opaque captures
1 parent 52f8aec commit da312fc

22 files changed

+221
-205
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,26 @@
1-
A lifetime bound on a trait implementation was captured at an incorrect place.
1+
An `impl Trait` captured a higher-ranked lifetime, which is not supported.
2+
3+
Currently, `impl Trait` typess are only allowed to capture lifetimes from
4+
their parent items, and not from any `for<'a>` binders in scope.
25

36
Erroneous code example:
47

58
```compile_fail,E0657
6-
trait Id<T> {}
7-
trait Lt<'a> {}
8-
9-
impl<'a> Lt<'a> for () {}
10-
impl<T> Id<T> for T {}
11-
12-
fn free_fn_capture_hrtb_in_impl_trait()
13-
-> Box<for<'a> Id<impl Lt<'a>>> // error!
14-
{
15-
Box::new(())
16-
}
9+
trait BorrowInto<'a> {
10+
type Target;
1711
18-
struct Foo;
19-
impl Foo {
20-
fn impl_fn_capture_hrtb_in_impl_trait()
21-
-> Box<for<'a> Id<impl Lt<'a>>> // error!
22-
{
23-
Box::new(())
24-
}
12+
fn borrow_into(&'a self) -> Self::Target;
2513
}
26-
```
2714
28-
Here, you have used the inappropriate lifetime in the `impl Trait`,
29-
The `impl Trait` can only capture lifetimes bound at the fn or impl
30-
level.
15+
impl<'a> BorrowInto<'a> for () {
16+
type Target = &'a ();
3117
32-
To fix this we have to define the lifetime at the function or impl
33-
level and use that lifetime in the `impl Trait`. For example you can
34-
define the lifetime at the function:
35-
36-
```
37-
trait Id<T> {}
38-
trait Lt<'a> {}
39-
40-
impl<'a> Lt<'a> for () {}
41-
impl<T> Id<T> for T {}
42-
43-
fn free_fn_capture_hrtb_in_impl_trait<'b>()
44-
-> Box<for<'a> Id<impl Lt<'b>>> // ok!
45-
{
46-
Box::new(())
18+
fn borrow_into(&'a self) -> Self::Target {
19+
self
20+
}
4721
}
4822
49-
struct Foo;
50-
impl Foo {
51-
fn impl_fn_capture_hrtb_in_impl_trait<'b>()
52-
-> Box<for<'a> Id<impl Lt<'b>>> // ok!
53-
{
54-
Box::new(())
55-
}
23+
fn opaque() -> impl for<'a> BorrowInto<'a, Target = impl Sized + 'a> {
24+
()
5625
}
5726
```

compiler/rustc_hir_analysis/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ hir_analysis_only_current_traits_primitive = only traits defined in the current
312312
313313
hir_analysis_only_current_traits_ty = `{$ty}` is not defined in the current crate
314314
315+
hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot capture {$bad_place}
316+
.label = `impl Trait` implicitly captures all lifetimes in scope
317+
.note = lifetime declared here
318+
315319
hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
316320
.help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
317321

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

+35-28
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
99
use rustc_ast::visit::walk_list;
1010
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
11-
use rustc_errors::{codes::*, struct_span_code_err};
1211
use rustc_hir as hir;
1312
use rustc_hir::def::{DefKind, Res};
1413
use rustc_hir::def_id::LocalDefId;
@@ -719,34 +718,42 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
719718
// and ban them. Type variables instantiated inside binders aren't
720719
// well-supported at the moment, so this doesn't work.
721720
// In the future, this should be fixed and this error should be removed.
722-
let def = self.map.defs.get(&lifetime.hir_id).cloned();
723-
let Some(ResolvedArg::LateBound(_, _, def_id)) = def else { continue };
724-
let Some(def_id) = def_id.as_local() else { continue };
725-
let hir_id = self.tcx.local_def_id_to_hir_id(def_id);
726-
// Ensure that the parent of the def is an item, not HRTB
727-
let parent_id = self.tcx.parent_hir_id(hir_id);
728-
if !parent_id.is_owner() {
729-
struct_span_code_err!(
730-
self.tcx.dcx(),
731-
lifetime.ident.span,
732-
E0657,
733-
"`impl Trait` can only capture lifetimes bound at the fn or impl level"
734-
)
735-
.emit();
736-
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
737-
}
738-
if let hir::Node::Item(hir::Item {
739-
kind: hir::ItemKind::OpaqueTy { .. }, ..
740-
}) = self.tcx.hir_node(parent_id)
721+
let def = self.map.defs.get(&lifetime.hir_id).copied();
722+
let Some(ResolvedArg::LateBound(_, _, lifetime_def_id)) = def else { continue };
723+
let Some(lifetime_def_id) = lifetime_def_id.as_local() else { continue };
724+
let lifetime_hir_id = self.tcx.local_def_id_to_hir_id(lifetime_def_id);
725+
726+
let bad_place = match self.tcx.hir_node(self.tcx.parent_hir_id(lifetime_hir_id))
741727
{
742-
self.tcx.dcx().struct_span_err(
743-
lifetime.ident.span,
744-
"higher kinded lifetime bounds on nested opaque types are not supported yet",
745-
)
746-
.with_span_note(self.tcx.def_span(def_id), "lifetime declared here")
747-
.emit();
748-
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
749-
}
728+
// Opaques do not declare their own lifetimes, so if a lifetime comes from an opaque
729+
// it must be a reified late-bound lifetime from a trait goal.
730+
hir::Node::Item(hir::Item {
731+
kind: hir::ItemKind::OpaqueTy { .. }, ..
732+
}) => "higher-ranked lifetime from outer `impl Trait`",
733+
// Other items are fine.
734+
hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => {
735+
continue;
736+
}
737+
hir::Node::Ty(_) => "higher-ranked lifetime from function pointer",
738+
_ => "higher-ranked lifetime",
739+
};
740+
741+
let (span, label) = if lifetime.ident.span == self.tcx.def_span(lifetime_def_id)
742+
{
743+
let opaque_span = self.tcx.def_span(item_id.owner_id);
744+
(opaque_span, Some(opaque_span))
745+
} else {
746+
(lifetime.ident.span, None)
747+
};
748+
749+
// Ensure that the parent of the def is an item, not HRTB
750+
self.tcx.dcx().emit_err(errors::OpaqueCapturesHigherRankedLifetime {
751+
span,
752+
label,
753+
decl_span: self.tcx.def_span(lifetime_def_id),
754+
bad_place,
755+
});
756+
self.uninsert_lifetime_on_error(lifetime, def.unwrap());
750757
}
751758
}
752759
_ => intravisit::walk_ty(self, ty),

compiler/rustc_hir_analysis/src/errors.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1607,3 +1607,15 @@ pub struct UnnamedFieldsReprFieldDefined {
16071607
#[primary_span]
16081608
pub span: Span,
16091609
}
1610+
1611+
#[derive(Diagnostic)]
1612+
#[diag(hir_analysis_opaque_captures_higher_ranked_lifetime, code = E0657)]
1613+
pub struct OpaqueCapturesHigherRankedLifetime {
1614+
#[primary_span]
1615+
pub span: Span,
1616+
#[label]
1617+
pub label: Option<Span>,
1618+
#[note]
1619+
pub decl_span: Span,
1620+
pub bad_place: &'static str,
1621+
}

0 commit comments

Comments
 (0)