Skip to content

Commit daec806

Browse files
Better error message for late/early lifetime param mismatch
1 parent ae64af9 commit daec806

File tree

6 files changed

+389
-49
lines changed

6 files changed

+389
-49
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

Lines changed: 232 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::iter;
55
use hir::def_id::{DefId, DefIdMap, LocalDefId};
66
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
77
use rustc_errors::codes::*;
8-
use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
8+
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err};
99
use rustc_hir::def::{DefKind, Res};
1010
use rustc_hir::intravisit::VisitorExt;
1111
use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit};
@@ -14,10 +14,10 @@ use rustc_infer::traits::util;
1414
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1515
use rustc_middle::ty::{
1616
self, BottomUpFolder, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeFolder,
17-
TypeSuperFoldable, TypeVisitableExt, TypingMode, Upcast,
17+
TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
1818
};
1919
use rustc_middle::{bug, span_bug};
20-
use rustc_span::Span;
20+
use rustc_span::{DUMMY_SP, Span};
2121
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
2222
use rustc_trait_selection::infer::InferCtxtExt;
2323
use rustc_trait_selection::regions::InferCtxtRegionExt;
@@ -1141,6 +1141,10 @@ fn check_region_bounds_on_impl_item<'tcx>(
11411141
return Ok(());
11421142
}
11431143

1144+
if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) {
1145+
return Err(guar);
1146+
}
1147+
11441148
let span = tcx
11451149
.hir_get_generics(impl_m.def_id.expect_local())
11461150
.expect("expected impl item to have generics or else we can't compare them")
@@ -1221,6 +1225,231 @@ fn check_region_bounds_on_impl_item<'tcx>(
12211225
Err(reported)
12221226
}
12231227

1228+
#[allow(unused)]
1229+
enum LateEarlyMismatch<'tcx> {
1230+
EarlyInImpl(DefId, DefId, ty::Region<'tcx>),
1231+
LateInImpl(DefId, DefId, ty::Region<'tcx>),
1232+
}
1233+
1234+
fn check_region_late_boundedness<'tcx>(
1235+
tcx: TyCtxt<'tcx>,
1236+
impl_m: ty::AssocItem,
1237+
trait_m: ty::AssocItem,
1238+
) -> Option<ErrorGuaranteed> {
1239+
if !impl_m.is_fn() {
1240+
return None;
1241+
}
1242+
1243+
let (infcx, param_env) = tcx
1244+
.infer_ctxt()
1245+
.build_with_typing_env(ty::TypingEnv::non_body_analysis(tcx, impl_m.def_id));
1246+
1247+
let impl_m_args = infcx.fresh_args_for_item(DUMMY_SP, impl_m.def_id);
1248+
let impl_m_sig = tcx.fn_sig(impl_m.def_id).instantiate(tcx, impl_m_args);
1249+
let impl_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, impl_m_sig);
1250+
1251+
let trait_m_args = infcx.fresh_args_for_item(DUMMY_SP, trait_m.def_id);
1252+
let trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_args);
1253+
let trait_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_m_sig);
1254+
1255+
let ocx = ObligationCtxt::new(&infcx);
1256+
let Ok(()) = ocx.eq(
1257+
&ObligationCause::dummy(),
1258+
param_env,
1259+
ty::Binder::dummy(trait_m_sig),
1260+
ty::Binder::dummy(impl_m_sig),
1261+
) else {
1262+
return None;
1263+
};
1264+
1265+
let errors = ocx.select_where_possible();
1266+
if !errors.is_empty() {
1267+
return None;
1268+
}
1269+
1270+
let mut mismatched = vec![];
1271+
1272+
let impl_generics = tcx.generics_of(impl_m.def_id);
1273+
for (id_arg, arg) in
1274+
std::iter::zip(ty::GenericArgs::identity_for_item(tcx, impl_m.def_id), impl_m_args)
1275+
{
1276+
if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
1277+
&& let ty::ReVar(vid) = r.kind()
1278+
&& let r = infcx
1279+
.inner
1280+
.borrow_mut()
1281+
.unwrap_region_constraints()
1282+
.opportunistic_resolve_var(tcx, vid)
1283+
&& let ty::ReLateParam(ty::LateParamRegion {
1284+
kind: ty::LateParamRegionKind::Named(trait_param_def_id, _),
1285+
..
1286+
}) = r.kind()
1287+
&& let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
1288+
{
1289+
mismatched.push(LateEarlyMismatch::EarlyInImpl(
1290+
impl_generics.region_param(ebr, tcx).def_id,
1291+
trait_param_def_id,
1292+
id_arg.expect_region(),
1293+
));
1294+
}
1295+
}
1296+
1297+
let trait_generics = tcx.generics_of(trait_m.def_id);
1298+
for (id_arg, arg) in
1299+
std::iter::zip(ty::GenericArgs::identity_for_item(tcx, trait_m.def_id), trait_m_args)
1300+
{
1301+
if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
1302+
&& let ty::ReVar(vid) = r.kind()
1303+
&& let r = infcx
1304+
.inner
1305+
.borrow_mut()
1306+
.unwrap_region_constraints()
1307+
.opportunistic_resolve_var(tcx, vid)
1308+
&& let ty::ReLateParam(ty::LateParamRegion {
1309+
kind: ty::LateParamRegionKind::Named(impl_param_def_id, _),
1310+
..
1311+
}) = r.kind()
1312+
&& let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
1313+
{
1314+
mismatched.push(LateEarlyMismatch::LateInImpl(
1315+
impl_param_def_id,
1316+
trait_generics.region_param(ebr, tcx).def_id,
1317+
id_arg.expect_region(),
1318+
));
1319+
}
1320+
}
1321+
1322+
if mismatched.is_empty() {
1323+
return None;
1324+
}
1325+
1326+
let spans: Vec<_> = mismatched
1327+
.iter()
1328+
.map(|param| {
1329+
let (LateEarlyMismatch::EarlyInImpl(impl_param_def_id, ..)
1330+
| LateEarlyMismatch::LateInImpl(impl_param_def_id, ..)) = param;
1331+
tcx.def_span(impl_param_def_id)
1332+
})
1333+
.collect();
1334+
1335+
let mut diag = tcx
1336+
.dcx()
1337+
.struct_span_err(
1338+
spans,
1339+
"lifetime parameters do not match the trait definition, \
1340+
since they differ in late-boundedness",
1341+
)
1342+
.with_code(E0195);
1343+
for mismatch in mismatched {
1344+
match mismatch {
1345+
LateEarlyMismatch::EarlyInImpl(
1346+
impl_param_def_id,
1347+
trait_param_def_id,
1348+
early_bound_region,
1349+
) => {
1350+
let mut multispan = MultiSpan::from_spans(vec![
1351+
tcx.def_span(impl_param_def_id),
1352+
tcx.def_span(trait_param_def_id),
1353+
]);
1354+
multispan
1355+
.push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl...");
1356+
multispan
1357+
.push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait...");
1358+
multispan.push_span_label(
1359+
tcx.def_span(impl_param_def_id),
1360+
format!("`{}` is early-bound", tcx.item_name(impl_param_def_id)),
1361+
);
1362+
multispan.push_span_label(
1363+
tcx.def_span(trait_param_def_id),
1364+
format!("`{}` is late-bound", tcx.item_name(trait_param_def_id)),
1365+
);
1366+
if let Some(span) =
1367+
find_region_in_predicates(tcx, impl_m.def_id, early_bound_region)
1368+
{
1369+
multispan.push_span_label(
1370+
span,
1371+
format!(
1372+
"this lifetime bound makes `{}` early-bound",
1373+
tcx.item_name(impl_param_def_id)
1374+
),
1375+
);
1376+
}
1377+
diag.span_note(
1378+
multispan,
1379+
format!(
1380+
"the late-boundedness of `{}` differs",
1381+
tcx.item_name(impl_param_def_id)
1382+
),
1383+
);
1384+
}
1385+
LateEarlyMismatch::LateInImpl(
1386+
impl_param_def_id,
1387+
trait_param_def_id,
1388+
early_bound_region,
1389+
) => {
1390+
let mut multispan = MultiSpan::from_spans(vec![
1391+
tcx.def_span(impl_param_def_id),
1392+
tcx.def_span(trait_param_def_id),
1393+
]);
1394+
multispan
1395+
.push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl...");
1396+
multispan
1397+
.push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait...");
1398+
multispan.push_span_label(
1399+
tcx.def_span(impl_param_def_id),
1400+
format!("`{}` is late-bound", tcx.item_name(impl_param_def_id)),
1401+
);
1402+
multispan.push_span_label(
1403+
tcx.def_span(trait_param_def_id),
1404+
format!("`{}` is early-bound", tcx.item_name(trait_param_def_id)),
1405+
);
1406+
if let Some(span) =
1407+
find_region_in_predicates(tcx, trait_m.def_id, early_bound_region)
1408+
{
1409+
multispan.push_span_label(
1410+
span,
1411+
format!(
1412+
"this lifetime bound makes `{}` early-bound",
1413+
tcx.item_name(trait_param_def_id)
1414+
),
1415+
);
1416+
}
1417+
diag.span_note(
1418+
multispan,
1419+
format!(
1420+
"the late-boundedness of `{}` differs",
1421+
tcx.item_name(impl_param_def_id)
1422+
),
1423+
);
1424+
}
1425+
}
1426+
}
1427+
1428+
Some(diag.emit())
1429+
}
1430+
1431+
fn find_region_in_predicates<'tcx>(
1432+
tcx: TyCtxt<'tcx>,
1433+
def_id: DefId,
1434+
early_bound_region: ty::Region<'tcx>,
1435+
) -> Option<Span> {
1436+
for (pred, span) in tcx.explicit_predicates_of(def_id).instantiate_identity(tcx) {
1437+
if pred.visit_with(&mut FindRegion(early_bound_region)).is_break() {
1438+
return Some(span);
1439+
}
1440+
}
1441+
1442+
struct FindRegion<'tcx>(ty::Region<'tcx>);
1443+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindRegion<'tcx> {
1444+
type Result = ControlFlow<()>;
1445+
fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
1446+
if r == self.0 { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
1447+
}
1448+
}
1449+
1450+
None
1451+
}
1452+
12241453
#[instrument(level = "debug", skip(infcx))]
12251454
fn extract_spans_for_error_reporting<'tcx>(
12261455
infcx: &infer::InferCtxt<'tcx>,

tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ pub trait Foo<'a, 't> {
1717

1818
impl<'a, 't> Foo<'a, 't> for &'a isize {
1919
fn no_bound<'b:'a>(self, b: Inv<'b>) {
20-
//~^ ERROR lifetime parameters or bounds on method `no_bound` do not match
20+
//~^ ERROR lifetime parameters do not match the trait definition, since they differ in late-boundedness
2121
}
2222

2323
fn has_bound<'b>(self, b: Inv<'b>) {
24-
//~^ ERROR lifetime parameters or bounds on method `has_bound` do not match
24+
//~^ ERROR lifetime parameters do not match the trait definition, since they differ in late-boundedness
2525
}
2626

2727
fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
@@ -40,7 +40,7 @@ impl<'a, 't> Foo<'a, 't> for &'a isize {
4040
}
4141

4242
fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
43-
//~^ ERROR lifetime parameters or bounds on method `wrong_bound2` do not match the trait
43+
//~^ ERROR lifetime parameters do not match the trait definition, since they differ in late-boundedness
4444
}
4545

4646
fn okay_bound<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) {

tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,46 @@
1-
error[E0195]: lifetime parameters or bounds on method `no_bound` do not match the trait declaration
2-
--> $DIR/regions-bound-missing-bound-in-impl.rs:19:16
1+
error[E0195]: lifetime parameters do not match the trait definition, since they differ in late-boundedness
2+
--> $DIR/regions-bound-missing-bound-in-impl.rs:19:17
33
|
4+
LL | fn no_bound<'b:'a>(self, b: Inv<'b>) {
5+
| ^^
6+
|
7+
note: the late-boundedness of `'b` differs
8+
--> $DIR/regions-bound-missing-bound-in-impl.rs:10:17
9+
|
10+
LL | pub trait Foo<'a, 't> {
11+
| --------------------- in this trait...
412
LL | fn no_bound<'b>(self, b: Inv<'b>);
5-
| ---- lifetimes in impl do not match this method in trait
13+
| ^^ `'b` is late-bound
614
...
15+
LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
16+
| -------------------------------------- in this impl...
717
LL | fn no_bound<'b:'a>(self, b: Inv<'b>) {
8-
| ^^^^^^^ lifetimes do not match method in trait
18+
| ^^ -- this lifetime bound makes `'b` early-bound
19+
| |
20+
| `'b` is early-bound
921

10-
error[E0195]: lifetime parameters or bounds on method `has_bound` do not match the trait declaration
11-
--> $DIR/regions-bound-missing-bound-in-impl.rs:23:17
22+
error[E0195]: lifetime parameters do not match the trait definition, since they differ in late-boundedness
23+
--> $DIR/regions-bound-missing-bound-in-impl.rs:23:18
24+
|
25+
LL | fn has_bound<'b>(self, b: Inv<'b>) {
26+
| ^^
1227
|
28+
note: the late-boundedness of `'b` differs
29+
--> $DIR/regions-bound-missing-bound-in-impl.rs:11:18
30+
|
31+
LL | pub trait Foo<'a, 't> {
32+
| --------------------- in this trait...
33+
LL | fn no_bound<'b>(self, b: Inv<'b>);
1334
LL | fn has_bound<'b:'a>(self, b: Inv<'b>);
14-
| -------
15-
| | |
16-
| | this bound might be missing in the impl
17-
| lifetimes in impl do not match this method in trait
35+
| ^^ -- this lifetime bound makes `'b` early-bound
36+
| |
37+
| `'b` is early-bound
38+
...
39+
LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
40+
| -------------------------------------- in this impl...
1841
...
1942
LL | fn has_bound<'b>(self, b: Inv<'b>) {
20-
| ^^^^ lifetimes do not match method in trait
43+
| ^^ `'b` is late-bound
2144

2245
error[E0308]: method not compatible with trait
2346
--> $DIR/regions-bound-missing-bound-in-impl.rs:27:5
@@ -57,18 +80,44 @@ note: ...does not necessarily outlive the lifetime `'c` as defined here
5780
LL | fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
5881
| ^^
5982

60-
error[E0195]: lifetime parameters or bounds on method `wrong_bound2` do not match the trait declaration
61-
--> $DIR/regions-bound-missing-bound-in-impl.rs:42:20
83+
error[E0195]: lifetime parameters do not match the trait definition, since they differ in late-boundedness
84+
--> $DIR/regions-bound-missing-bound-in-impl.rs:42:30
85+
|
86+
LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
87+
| ^^^ ^^^
6288
|
89+
note: the late-boundedness of `'_` differs
90+
--> $DIR/regions-bound-missing-bound-in-impl.rs:13:21
91+
|
92+
LL | pub trait Foo<'a, 't> {
93+
| --------------------- in this trait...
94+
...
6395
LL | fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
64-
| ----------------
65-
| | | |
66-
| | | this bound might be missing in the impl
67-
| | this bound might be missing in the impl
68-
| lifetimes in impl do not match this method in trait
96+
| ^^ -- this lifetime bound makes `'b` early-bound
97+
| |
98+
| `'b` is early-bound
99+
...
100+
LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
101+
| -------------------------------------- in this impl...
102+
...
103+
LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
104+
| ^^^ `'_` is late-bound
105+
note: the late-boundedness of `'_` differs
106+
--> $DIR/regions-bound-missing-bound-in-impl.rs:13:27
107+
|
108+
LL | pub trait Foo<'a, 't> {
109+
| --------------------- in this trait...
110+
...
111+
LL | fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
112+
| ^^ -- this lifetime bound makes `'d` early-bound
113+
| |
114+
| `'d` is early-bound
115+
...
116+
LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
117+
| -------------------------------------- in this impl...
69118
...
70119
LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
71-
| ^ lifetimes do not match method in trait
120+
| ^^^ `'_` is late-bound
72121

73122
error[E0276]: impl has stricter requirements than trait
74123
--> $DIR/regions-bound-missing-bound-in-impl.rs:49:26

0 commit comments

Comments
 (0)