Skip to content

Commit 03bee1e

Browse files
Suggest using precise capturing for hidden type that captures region
1 parent 0c81f94 commit 03bee1e

File tree

4 files changed

+212
-15
lines changed

4 files changed

+212
-15
lines changed

compiler/rustc_infer/src/infer/error_reporting/region.rs

+112-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::iter;
22

3+
use rustc_data_structures::fx::FxIndexSet;
34
use rustc_errors::{
45
struct_span_code_err, Applicability, Diag, Subdiagnostic, E0309, E0310, E0311, E0495,
56
};
@@ -12,7 +13,7 @@ use rustc_middle::traits::ObligationCauseCode;
1213
use rustc_middle::ty::error::TypeError;
1314
use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _};
1415
use rustc_span::symbol::kw;
15-
use rustc_span::{ErrorGuaranteed, Span};
16+
use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol};
1617
use rustc_type_ir::Upcast as _;
1718

1819
use super::nice_region_error::find_anon_type;
@@ -1201,17 +1202,21 @@ pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(
12011202
"",
12021203
);
12031204
if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) {
1204-
let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id);
1205-
nice_region_error::suggest_new_region_bound(
1206-
tcx,
1207-
&mut err,
1208-
fn_returns,
1209-
hidden_region.to_string(),
1210-
None,
1211-
format!("captures `{hidden_region}`"),
1212-
None,
1213-
Some(reg_info.def_id),
1214-
)
1205+
if infcx.tcx.features().precise_capturing {
1206+
suggest_precise_capturing(tcx, opaque_ty_key.def_id, hidden_region, &mut err);
1207+
} else {
1208+
let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id);
1209+
nice_region_error::suggest_new_region_bound(
1210+
tcx,
1211+
&mut err,
1212+
fn_returns,
1213+
hidden_region.to_string(),
1214+
None,
1215+
format!("captures `{hidden_region}`"),
1216+
None,
1217+
Some(reg_info.def_id),
1218+
)
1219+
}
12151220
}
12161221
}
12171222
ty::RePlaceholder(_) => {
@@ -1257,3 +1262,98 @@ pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(
12571262

12581263
err
12591264
}
1265+
1266+
fn suggest_precise_capturing<'tcx>(
1267+
tcx: TyCtxt<'tcx>,
1268+
opaque_def_id: LocalDefId,
1269+
captured_lifetime: ty::Region<'tcx>,
1270+
diag: &mut Diag<'_>,
1271+
) {
1272+
let hir::OpaqueTy { bounds, .. } =
1273+
tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
1274+
1275+
let new_lifetime = Symbol::intern(&captured_lifetime.to_string());
1276+
1277+
if let Some((args, span)) = bounds.iter().find_map(|bound| match bound {
1278+
hir::GenericBound::Use(args, span) => Some((args, span)),
1279+
_ => None,
1280+
}) {
1281+
let last_lifetime_span = args.iter().rev().find_map(|arg| match arg {
1282+
hir::PreciseCapturingArg::Lifetime(lt) => Some(lt.ident.span),
1283+
_ => None,
1284+
});
1285+
1286+
let first_param_span = args.iter().find_map(|arg| match arg {
1287+
hir::PreciseCapturingArg::Param(p) => Some(p.ident.span),
1288+
_ => None,
1289+
});
1290+
1291+
let (insertion_span, pre, post) = if let Some(last_lifetime_span) = last_lifetime_span {
1292+
(last_lifetime_span.shrink_to_hi(), ", ", "")
1293+
} else if let Some(first_param_span) = first_param_span {
1294+
(first_param_span.shrink_to_lo(), "", ", ")
1295+
} else {
1296+
(span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), "", "")
1297+
};
1298+
1299+
diag.span_suggestion_verbose(
1300+
insertion_span,
1301+
format!("add `{new_lifetime}` to the `use<...>` bound to explicitly capture it",),
1302+
format!("{pre}{new_lifetime}{post}"),
1303+
Applicability::MachineApplicable,
1304+
);
1305+
} else {
1306+
let mut captured_lifetimes = FxIndexSet::default();
1307+
let mut captured_non_lifetimes = FxIndexSet::default();
1308+
1309+
let variances = tcx.variances_of(opaque_def_id);
1310+
let mut generics = tcx.generics_of(opaque_def_id);
1311+
loop {
1312+
for param in &generics.own_params {
1313+
if variances[param.index as usize] == ty::Bivariant {
1314+
continue;
1315+
}
1316+
1317+
match param.kind {
1318+
ty::GenericParamDefKind::Lifetime => {
1319+
captured_lifetimes.insert(param.name);
1320+
}
1321+
ty::GenericParamDefKind::Type { synthetic: true, .. } => {
1322+
// FIXME: We can't provide a good suggestion for
1323+
// `use<...>` if we have an APIT. Bail for now.
1324+
return;
1325+
}
1326+
ty::GenericParamDefKind::Type { .. }
1327+
| ty::GenericParamDefKind::Const { .. } => {
1328+
captured_non_lifetimes.insert(param.name);
1329+
}
1330+
}
1331+
}
1332+
1333+
if let Some(parent) = generics.parent {
1334+
generics = tcx.generics_of(parent);
1335+
} else {
1336+
break;
1337+
}
1338+
}
1339+
1340+
if !captured_lifetimes.insert(new_lifetime) {
1341+
// Uh, strange. This lifetime appears to already be captured...
1342+
return;
1343+
}
1344+
1345+
let concatenated_bounds = captured_lifetimes
1346+
.into_iter()
1347+
.chain(captured_non_lifetimes)
1348+
.map(|sym| sym.to_string())
1349+
.collect::<Vec<_>>()
1350+
.join(", ");
1351+
1352+
diag.span_suggestion_verbose(
1353+
tcx.def_span(opaque_def_id).shrink_to_hi(),
1354+
format!("add a `use<...>` bound to explicitly capture `{new_lifetime}`",),
1355+
format!(" + use<{concatenated_bounds}>"),
1356+
Applicability::MachineApplicable,
1357+
);
1358+
}
1359+
}

tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<> { x }
1616
| | opaque type defined here
1717
| hidden type `&'a ()` captures the lifetime `'a` as defined here
1818
|
19-
help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound
19+
help: add `'a` to the `use<...>` bound to explicitly capture it
2020
|
21-
LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<> + 'a { x }
22-
| ++++
21+
LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<'a> { x }
22+
| ++
2323

2424
error: aborting due to 2 previous errors
2525

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![feature(precise_capturing)]
2+
3+
fn lifetime<'a, 'b>(x: &'a ()) -> impl Sized + use<'b> {
4+
//~^ HELP add `'a` to the `use<...>` bound
5+
x
6+
//~^ ERROR hidden type for
7+
}
8+
9+
fn param<'a, T>(x: &'a ()) -> impl Sized + use<T> {
10+
//~^ HELP add `'a` to the `use<...>` bound
11+
x
12+
//~^ ERROR hidden type for
13+
}
14+
15+
fn empty<'a>(x: &'a ()) -> impl Sized + use<> {
16+
//~^ HELP add `'a` to the `use<...>` bound
17+
x
18+
//~^ ERROR hidden type for
19+
}
20+
21+
trait Captures<'a> {}
22+
impl<T> Captures<'_> for T {}
23+
24+
fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'captured> {
25+
//~^ HELP add a `use<...>` bound
26+
x
27+
//~^ ERROR hidden type for
28+
}
29+
30+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
2+
--> $DIR/hidden-type-suggestion.rs:5:5
3+
|
4+
LL | fn lifetime<'a, 'b>(x: &'a ()) -> impl Sized + use<'b> {
5+
| -- -------------------- opaque type defined here
6+
| |
7+
| hidden type `&'a ()` captures the lifetime `'a` as defined here
8+
LL |
9+
LL | x
10+
| ^
11+
|
12+
help: add `'a` to the `use<...>` bound to explicitly capture it
13+
|
14+
LL | fn lifetime<'a, 'b>(x: &'a ()) -> impl Sized + use<'b, 'a> {
15+
| ++++
16+
17+
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
18+
--> $DIR/hidden-type-suggestion.rs:11:5
19+
|
20+
LL | fn param<'a, T>(x: &'a ()) -> impl Sized + use<T> {
21+
| -- ------------------- opaque type defined here
22+
| |
23+
| hidden type `&'a ()` captures the lifetime `'a` as defined here
24+
LL |
25+
LL | x
26+
| ^
27+
|
28+
help: add `'a` to the `use<...>` bound to explicitly capture it
29+
|
30+
LL | fn param<'a, T>(x: &'a ()) -> impl Sized + use<'a, T> {
31+
| +++
32+
33+
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
34+
--> $DIR/hidden-type-suggestion.rs:17:5
35+
|
36+
LL | fn empty<'a>(x: &'a ()) -> impl Sized + use<> {
37+
| -- ------------------ opaque type defined here
38+
| |
39+
| hidden type `&'a ()` captures the lifetime `'a` as defined here
40+
LL |
41+
LL | x
42+
| ^
43+
|
44+
help: add `'a` to the `use<...>` bound to explicitly capture it
45+
|
46+
LL | fn empty<'a>(x: &'a ()) -> impl Sized + use<'a> {
47+
| ++
48+
49+
error[E0700]: hidden type for `impl Captures<'captured>` captures lifetime that does not appear in bounds
50+
--> $DIR/hidden-type-suggestion.rs:26:5
51+
|
52+
LL | fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'captured> {
53+
| -- ------------------------ opaque type defined here
54+
| |
55+
| hidden type `&'a ()` captures the lifetime `'a` as defined here
56+
LL |
57+
LL | x
58+
| ^
59+
|
60+
help: add a `use<...>` bound to explicitly capture `'a`
61+
|
62+
LL | fn missing<'a, 'captured, 'not_captured, Captured>(x: &'a ()) -> impl Captures<'captured> + use<'captured, 'a, Captured> {
63+
| ++++++++++++++++++++++++++++++
64+
65+
error: aborting due to 4 previous errors
66+
67+
For more information about this error, try `rustc --explain E0700`.

0 commit comments

Comments
 (0)