Skip to content

Commit 20cf4eb

Browse files
Rollup merge of #127619 - compiler-errors:precise-capturing-better-sugg, r=oli-obk
Suggest using precise capturing for hidden type that captures region Adjusts the "add `+ '_`" suggestion for opaques to instead suggest adding or reusing the `+ use<>` in the opaque. r? oli-obk or please re-roll if you're busy!
2 parents 2e0591b + 42653c0 commit 20cf4eb

File tree

6 files changed

+242
-15
lines changed

6 files changed

+242
-15
lines changed

compiler/rustc_infer/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ infer_opaque_hidden_type =
221221
222222
infer_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type
223223
infer_outlives_content = lifetime of reference outlives lifetime of borrowed content...
224+
225+
infer_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it
226+
infer_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}`
227+
224228
infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here...
225229
infer_prlf_defined_without_sub = the lifetime defined here...
226230
infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)

compiler/rustc_infer/src/errors/mod.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1581,3 +1581,32 @@ pub enum ObligationCauseFailureCode {
15811581
subdiags: Vec<TypeErrorAdditionalDiags>,
15821582
},
15831583
}
1584+
1585+
#[derive(Subdiagnostic)]
1586+
pub enum AddPreciseCapturing {
1587+
#[suggestion(
1588+
infer_precise_capturing_new,
1589+
style = "verbose",
1590+
code = " + use<{concatenated_bounds}>",
1591+
applicability = "machine-applicable"
1592+
)]
1593+
New {
1594+
#[primary_span]
1595+
span: Span,
1596+
new_lifetime: Symbol,
1597+
concatenated_bounds: String,
1598+
},
1599+
#[suggestion(
1600+
infer_precise_capturing_existing,
1601+
style = "verbose",
1602+
code = "{pre}{new_lifetime}{post}",
1603+
applicability = "machine-applicable"
1604+
)]
1605+
Existing {
1606+
#[primary_span]
1607+
span: Span,
1608+
new_lifetime: Symbol,
1609+
pre: &'static str,
1610+
post: &'static str,
1611+
},
1612+
}

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

+109-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,95 @@ 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 (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+
// If we have no args, then have `use<>` and need to fall back to using
1297+
// span math. This sucks, but should be reliable due to the construction
1298+
// of the `use<>` span.
1299+
(span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), "", "")
1300+
};
1301+
1302+
diag.subdiagnostic(errors::AddPreciseCapturing::Existing { span, new_lifetime, pre, post });
1303+
} else {
1304+
let mut captured_lifetimes = FxIndexSet::default();
1305+
let mut captured_non_lifetimes = FxIndexSet::default();
1306+
1307+
let variances = tcx.variances_of(opaque_def_id);
1308+
let mut generics = tcx.generics_of(opaque_def_id);
1309+
loop {
1310+
for param in &generics.own_params {
1311+
if variances[param.index as usize] == ty::Bivariant {
1312+
continue;
1313+
}
1314+
1315+
match param.kind {
1316+
ty::GenericParamDefKind::Lifetime => {
1317+
captured_lifetimes.insert(param.name);
1318+
}
1319+
ty::GenericParamDefKind::Type { synthetic: true, .. } => {
1320+
// FIXME: We can't provide a good suggestion for
1321+
// `use<...>` if we have an APIT. Bail for now.
1322+
return;
1323+
}
1324+
ty::GenericParamDefKind::Type { .. }
1325+
| ty::GenericParamDefKind::Const { .. } => {
1326+
captured_non_lifetimes.insert(param.name);
1327+
}
1328+
}
1329+
}
1330+
1331+
if let Some(parent) = generics.parent {
1332+
generics = tcx.generics_of(parent);
1333+
} else {
1334+
break;
1335+
}
1336+
}
1337+
1338+
if !captured_lifetimes.insert(new_lifetime) {
1339+
// Uh, strange. This lifetime appears to already be captured...
1340+
return;
1341+
}
1342+
1343+
let concatenated_bounds = captured_lifetimes
1344+
.into_iter()
1345+
.chain(captured_non_lifetimes)
1346+
.map(|sym| sym.to_string())
1347+
.collect::<Vec<_>>()
1348+
.join(", ");
1349+
1350+
diag.subdiagnostic(errors::AddPreciseCapturing::New {
1351+
span: tcx.def_span(opaque_def_id).shrink_to_hi(),
1352+
new_lifetime,
1353+
concatenated_bounds,
1354+
});
1355+
}
1356+
}

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)