Skip to content

Commit bad5026

Browse files
committed
Auto merge of #147602 - JohnTitor:dedup-lifetime-capture-errors, r=estebank
Deduplicate higher-ranked lifetime capture errors in impl Trait Previously, when an `impl Trait` captured multiple higher-ranked lifetimes from an outer `impl Trait`, the compiler would emit a separate error for each captured lifetime. This resulted in verbose and confusing diagnostics, especially in edition 2024 where implicit captures caused duplicate errors. This commit introduces error accumulation that collects all capture spans and lifetime declaration spans, then emits a single consolidated diagnostic using MultiSpan. The new error shows all captured lifetimes with visual indicators and lists all declarations in a single note. r? `@estebank` Fixes #143022
2 parents 2e667b0 + e4194c7 commit bad5026

18 files changed

+185
-76
lines changed

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ struct BoundVarContext<'a, 'tcx> {
6666
rbv: &'a mut ResolveBoundVars,
6767
disambiguator: &'a mut DisambiguatorState,
6868
scope: ScopeRef<'a>,
69+
opaque_capture_errors: RefCell<Option<OpaqueHigherRankedLifetimeCaptureErrors>>,
70+
}
71+
72+
struct OpaqueHigherRankedLifetimeCaptureErrors {
73+
bad_place: &'static str,
74+
capture_spans: Vec<Span>,
75+
decl_spans: Vec<Span>,
6976
}
7077

7178
#[derive(Debug)]
@@ -253,6 +260,7 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou
253260
rbv: &mut rbv,
254261
scope: &Scope::Root { opt_parent_item: None },
255262
disambiguator: &mut DisambiguatorState::new(),
263+
opaque_capture_errors: RefCell::new(None),
256264
};
257265
match tcx.hir_owner_node(local_def_id) {
258266
hir::OwnerNode::Item(item) => visitor.visit_item(item),
@@ -597,6 +605,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
597605
})
598606
});
599607

608+
self.emit_opaque_capture_errors();
609+
600610
let captures = captures.into_inner().into_iter().collect();
601611
debug!(?captures);
602612
self.rbv.opaque_captured_lifetimes.insert(opaque.def_id, captures);
@@ -1089,12 +1099,20 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
10891099
F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>),
10901100
{
10911101
let BoundVarContext { tcx, rbv, disambiguator, .. } = self;
1092-
let mut this = BoundVarContext { tcx: *tcx, rbv, disambiguator, scope: &wrap_scope };
1102+
let nested_errors = RefCell::new(self.opaque_capture_errors.borrow_mut().take());
1103+
let mut this = BoundVarContext {
1104+
tcx: *tcx,
1105+
rbv,
1106+
disambiguator,
1107+
scope: &wrap_scope,
1108+
opaque_capture_errors: nested_errors,
1109+
};
10931110
let span = debug_span!("scope", scope = ?this.scope.debug_truncated());
10941111
{
10951112
let _enter = span.enter();
10961113
f(&mut this);
10971114
}
1115+
*self.opaque_capture_errors.borrow_mut() = this.opaque_capture_errors.into_inner();
10981116
}
10991117

11001118
fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec<ty::BoundVariableKind>) {
@@ -1424,21 +1442,52 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
14241442
};
14251443

14261444
let decl_span = self.tcx.def_span(lifetime_def_id);
1427-
let (span, label) = if capture_span != decl_span {
1428-
(capture_span, None)
1429-
} else {
1430-
let opaque_span = self.tcx.def_span(opaque_def_id);
1431-
(opaque_span, Some(opaque_span))
1432-
};
1445+
let opaque_span = self.tcx.def_span(opaque_def_id);
1446+
1447+
let mut errors = self.opaque_capture_errors.borrow_mut();
1448+
let error_info = errors.get_or_insert_with(|| OpaqueHigherRankedLifetimeCaptureErrors {
1449+
bad_place,
1450+
capture_spans: Vec::new(),
1451+
decl_spans: Vec::new(),
1452+
});
1453+
1454+
if error_info.capture_spans.is_empty() {
1455+
error_info.capture_spans.push(opaque_span);
1456+
}
1457+
1458+
if capture_span != decl_span && capture_span != opaque_span {
1459+
error_info.capture_spans.push(capture_span);
1460+
}
1461+
1462+
if !error_info.decl_spans.contains(&decl_span) {
1463+
error_info.decl_spans.push(decl_span);
1464+
}
1465+
1466+
// Errors should be emitted by `emit_opaque_capture_errors`.
1467+
Err(self.tcx.dcx().span_delayed_bug(capture_span, "opaque capture error not emitted"))
1468+
}
1469+
1470+
fn emit_opaque_capture_errors(&self) -> Option<ErrorGuaranteed> {
1471+
let errors = self.opaque_capture_errors.borrow_mut().take()?;
1472+
if errors.capture_spans.is_empty() {
1473+
return None;
1474+
}
1475+
1476+
let mut span = rustc_errors::MultiSpan::from_span(errors.capture_spans[0]);
1477+
for &capture_span in &errors.capture_spans[1..] {
1478+
span.push_span_label(capture_span, "");
1479+
}
1480+
let decl_span = rustc_errors::MultiSpan::from_spans(errors.decl_spans);
14331481

14341482
// Ensure that the parent of the def is an item, not HRTB
14351483
let guar = self.tcx.dcx().emit_err(errors::OpaqueCapturesHigherRankedLifetime {
14361484
span,
1437-
label,
1485+
label: Some(errors.capture_spans[0]),
14381486
decl_span,
1439-
bad_place,
1487+
bad_place: errors.bad_place,
14401488
});
1441-
Err(guar)
1489+
1490+
Some(guar)
14421491
}
14431492

14441493
#[instrument(level = "trace", skip(self, opaque_capture_scopes), ret)]

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,11 +1551,11 @@ pub(crate) struct UnconstrainedGenericParameter {
15511551
#[diag(hir_analysis_opaque_captures_higher_ranked_lifetime, code = E0657)]
15521552
pub(crate) struct OpaqueCapturesHigherRankedLifetime {
15531553
#[primary_span]
1554-
pub span: Span,
1554+
pub span: MultiSpan,
15551555
#[label]
15561556
pub label: Option<Span>,
15571557
#[note]
1558-
pub decl_span: Span,
1558+
pub decl_span: MultiSpan,
15591559
pub bad_place: &'static str,
15601560
}
15611561

tests/ui/error-codes/E0657.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
2-
--> $DIR/E0657.rs:10:35
2+
--> $DIR/E0657.rs:10:27
33
|
44
LL | -> Box<dyn for<'a> Id<impl Lt<'a>>>
5-
| ^^
5+
| ^^^^^^^^--^ `impl Trait` implicitly captures all lifetimes in scope
66
|
77
note: lifetime declared here
88
--> $DIR/E0657.rs:10:20
@@ -11,10 +11,10 @@ LL | -> Box<dyn for<'a> Id<impl Lt<'a>>>
1111
| ^^
1212

1313
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type
14-
--> $DIR/E0657.rs:19:39
14+
--> $DIR/E0657.rs:19:31
1515
|
1616
LL | -> Box<dyn for<'a> Id<impl Lt<'a>>>
17-
| ^^
17+
| ^^^^^^^^--^ `impl Trait` implicitly captures all lifetimes in scope
1818
|
1919
note: lifetime declared here
2020
--> $DIR/E0657.rs:19:24
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
2+
--> $DIR/higher-ranked-lifetime-capture-deduplication.rs:19:44
3+
|
4+
LL | fn f() -> impl for<'a, 'b> Trait<'a, Out = impl Sized + 'a + 'b> {
5+
| ^^^^^^^^^^^^^--^^^--
6+
| |
7+
| `impl Trait` implicitly captures all lifetimes in scope
8+
|
9+
note: lifetime declared here
10+
--> $DIR/higher-ranked-lifetime-capture-deduplication.rs:19:20
11+
|
12+
LL | fn f() -> impl for<'a, 'b> Trait<'a, Out = impl Sized + 'a + 'b> {
13+
| ^^ ^^
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0657`.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
2+
--> $DIR/higher-ranked-lifetime-capture-deduplication.rs:19:44
3+
|
4+
LL | fn f() -> impl for<'a, 'b> Trait<'a, Out = impl Sized + 'a + 'b> {
5+
| ^^^^^^^^^^^^^--^^^--
6+
| |
7+
| `impl Trait` implicitly captures all lifetimes in scope
8+
|
9+
note: lifetime declared here
10+
--> $DIR/higher-ranked-lifetime-capture-deduplication.rs:19:20
11+
|
12+
LL | fn f() -> impl for<'a, 'b> Trait<'a, Out = impl Sized + 'a + 'b> {
13+
| ^^ ^^
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0657`.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@revisions: edition2015 edition2024
2+
//@[edition2015] edition:2015
3+
//@[edition2024] edition:2024
4+
5+
trait Trait<'a> {
6+
type Out;
7+
fn call(&'a self) -> Self::Out;
8+
}
9+
10+
struct X(());
11+
12+
impl<'a> Trait<'a> for X {
13+
type Out = ();
14+
fn call(&'a self) -> Self::Out {
15+
()
16+
}
17+
}
18+
19+
fn f() -> impl for<'a, 'b> Trait<'a, Out = impl Sized + 'a + 'b> {
20+
//~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
21+
X(())
22+
}
23+
24+
fn main() {
25+
let _ = f();
26+
}

tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ LL + fn d() -> impl Fn() -> (impl Debug + 'static) {
1212
|
1313

1414
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
15-
--> $DIR/impl-fn-hrtb-bounds.rs:4:41
15+
--> $DIR/impl-fn-hrtb-bounds.rs:4:28
1616
|
1717
LL | fn a() -> impl Fn(&u8) -> (impl Debug + '_) {
18-
| ^^
18+
| ^^^^^^^^^^^^^--
19+
| |
20+
| `impl Trait` implicitly captures all lifetimes in scope
1921
|
2022
note: lifetime declared here
2123
--> $DIR/impl-fn-hrtb-bounds.rs:4:19
@@ -24,10 +26,12 @@ LL | fn a() -> impl Fn(&u8) -> (impl Debug + '_) {
2426
| ^
2527

2628
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
27-
--> $DIR/impl-fn-hrtb-bounds.rs:9:52
29+
--> $DIR/impl-fn-hrtb-bounds.rs:9:39
2830
|
2931
LL | fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) {
30-
| ^^
32+
| ^^^^^^^^^^^^^--
33+
| |
34+
| `impl Trait` implicitly captures all lifetimes in scope
3135
|
3236
note: lifetime declared here
3337
--> $DIR/impl-fn-hrtb-bounds.rs:9:20
@@ -36,10 +40,12 @@ LL | fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) {
3640
| ^^
3741

3842
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
39-
--> $DIR/impl-fn-hrtb-bounds.rs:14:52
43+
--> $DIR/impl-fn-hrtb-bounds.rs:14:39
4044
|
4145
LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) {
42-
| ^^
46+
| ^^^^^^^^^^^^^--
47+
| |
48+
| `impl Trait` implicitly captures all lifetimes in scope
4349
|
4450
note: lifetime declared here
4551
--> $DIR/impl-fn-hrtb-bounds.rs:14:20

tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ LL | fn b() -> impl Fn() -> (impl Debug + Send) {
2121
| + +
2222

2323
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
24-
--> $DIR/impl-fn-parsing-ambiguities.rs:4:40
24+
--> $DIR/impl-fn-parsing-ambiguities.rs:4:27
2525
|
2626
LL | fn a() -> impl Fn(&u8) -> impl Debug + '_ {
27-
| ^^
27+
| ^^^^^^^^^^^^^--
28+
| |
29+
| `impl Trait` implicitly captures all lifetimes in scope
2830
|
2931
note: lifetime declared here
3032
--> $DIR/impl-fn-parsing-ambiguities.rs:4:19

tests/ui/impl-trait/issues/issue-54895.edition2015.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
2-
--> $DIR/issue-54895.rs:18:53
2+
--> $DIR/issue-54895.rs:18:40
33
|
44
LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
5-
| ^^
5+
| ^^^^^^^^^^^^^--
6+
| |
7+
| `impl Trait` implicitly captures all lifetimes in scope
68
|
79
note: lifetime declared here
810
--> $DIR/issue-54895.rs:18:20

tests/ui/impl-trait/issues/issue-54895.edition2024.stderr

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,16 @@ error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `imp
22
--> $DIR/issue-54895.rs:18:40
33
|
44
LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
5-
| ^^^^^^^^^^^^^^^ `impl Trait` implicitly captures all lifetimes in scope
5+
| ^^^^^^^^^^^^^--
6+
| |
7+
| `impl Trait` implicitly captures all lifetimes in scope
68
|
79
note: lifetime declared here
810
--> $DIR/issue-54895.rs:18:20
911
|
1012
LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
1113
| ^^
1214

13-
error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait`
14-
--> $DIR/issue-54895.rs:18:53
15-
|
16-
LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
17-
| ^^
18-
|
19-
note: lifetime declared here
20-
--> $DIR/issue-54895.rs:18:20
21-
|
22-
LL | fn f() -> impl for<'a> Trait<'a, Out = impl Sized + 'a> {
23-
| ^^
24-
25-
error: aborting due to 2 previous errors
15+
error: aborting due to 1 previous error
2616

2717
For more information about this error, try `rustc --explain E0657`.

0 commit comments

Comments
 (0)