Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit f03d246

Browse filesBrowse files
Better error message for late/early lifetime param mismatch
1 parent b27d630 commit f03d246
Copy full SHA for f03d246

File tree

Expand file treeCollapse file tree

6 files changed

+398
-49
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+398
-49
lines changed

‎compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

Copy file name to clipboardExpand all lines: compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+234-3Lines changed: 234 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,233 @@ 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+
1257+
// Equate the signatures so that we can infer whether a late-bound param was present where
1258+
// an early-bound param was expected, since we replace the late-bound lifetimes with
1259+
// `ReLateParam`, and early-bound lifetimes with infer vars, so the early-bound args will
1260+
// resolve to `ReLateParam` if there is a mismatch.
1261+
let Ok(()) = ocx.eq(
1262+
&ObligationCause::dummy(),
1263+
param_env,
1264+
ty::Binder::dummy(trait_m_sig),
1265+
ty::Binder::dummy(impl_m_sig),
1266+
) else {
1267+
return None;
1268+
};
1269+
1270+
let errors = ocx.select_where_possible();
1271+
if !errors.is_empty() {
1272+
return None;
1273+
}
1274+
1275+
let mut mismatched = vec![];
1276+
1277+
let impl_generics = tcx.generics_of(impl_m.def_id);
1278+
for (id_arg, arg) in
1279+
std::iter::zip(ty::GenericArgs::identity_for_item(tcx, impl_m.def_id), impl_m_args)
1280+
{
1281+
if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
1282+
&& let ty::ReVar(vid) = r.kind()
1283+
&& let r = infcx
1284+
.inner
1285+
.borrow_mut()
1286+
.unwrap_region_constraints()
1287+
.opportunistic_resolve_var(tcx, vid)
1288+
&& let ty::ReLateParam(ty::LateParamRegion {
1289+
kind: ty::LateParamRegionKind::Named(trait_param_def_id, _),
1290+
..
1291+
}) = r.kind()
1292+
&& let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
1293+
{
1294+
mismatched.push(LateEarlyMismatch::EarlyInImpl(
1295+
impl_generics.region_param(ebr, tcx).def_id,
1296+
trait_param_def_id,
1297+
id_arg.expect_region(),
1298+
));
1299+
}
1300+
}
1301+
1302+
let trait_generics = tcx.generics_of(trait_m.def_id);
1303+
for (id_arg, arg) in
1304+
std::iter::zip(ty::GenericArgs::identity_for_item(tcx, trait_m.def_id), trait_m_args)
1305+
{
1306+
if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
1307+
&& let ty::ReVar(vid) = r.kind()
1308+
&& let r = infcx
1309+
.inner
1310+
.borrow_mut()
1311+
.unwrap_region_constraints()
1312+
.opportunistic_resolve_var(tcx, vid)
1313+
&& let ty::ReLateParam(ty::LateParamRegion {
1314+
kind: ty::LateParamRegionKind::Named(impl_param_def_id, _),
1315+
..
1316+
}) = r.kind()
1317+
&& let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
1318+
{
1319+
mismatched.push(LateEarlyMismatch::LateInImpl(
1320+
impl_param_def_id,
1321+
trait_generics.region_param(ebr, tcx).def_id,
1322+
id_arg.expect_region(),
1323+
));
1324+
}
1325+
}
1326+
1327+
if mismatched.is_empty() {
1328+
return None;
1329+
}
1330+
1331+
let spans: Vec<_> = mismatched
1332+
.iter()
1333+
.map(|param| {
1334+
let (LateEarlyMismatch::EarlyInImpl(impl_param_def_id, ..)
1335+
| LateEarlyMismatch::LateInImpl(impl_param_def_id, ..)) = param;
1336+
tcx.def_span(impl_param_def_id)
1337+
})
1338+
.collect();
1339+
1340+
let mut diag = tcx
1341+
.dcx()
1342+
.struct_span_err(spans, "lifetime parameters do not match the trait definition")
1343+
.with_note("lifetime parameters differ in whether they are early- or late-bound")
1344+
.with_code(E0195);
1345+
for mismatch in mismatched {
1346+
match mismatch {
1347+
LateEarlyMismatch::EarlyInImpl(
1348+
impl_param_def_id,
1349+
trait_param_def_id,
1350+
early_bound_region,
1351+
) => {
1352+
let mut multispan = MultiSpan::from_spans(vec![
1353+
tcx.def_span(impl_param_def_id),
1354+
tcx.def_span(trait_param_def_id),
1355+
]);
1356+
multispan
1357+
.push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl...");
1358+
multispan
1359+
.push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait...");
1360+
multispan.push_span_label(
1361+
tcx.def_span(impl_param_def_id),
1362+
format!("`{}` is early-bound", tcx.item_name(impl_param_def_id)),
1363+
);
1364+
multispan.push_span_label(
1365+
tcx.def_span(trait_param_def_id),
1366+
format!("`{}` is late-bound", tcx.item_name(trait_param_def_id)),
1367+
);
1368+
if let Some(span) =
1369+
find_region_in_predicates(tcx, impl_m.def_id, early_bound_region)
1370+
{
1371+
multispan.push_span_label(
1372+
span,
1373+
format!(
1374+
"this lifetime bound makes `{}` early-bound",
1375+
tcx.item_name(impl_param_def_id)
1376+
),
1377+
);
1378+
}
1379+
diag.span_note(
1380+
multispan,
1381+
format!(
1382+
"`{}` differs between the trait and impl",
1383+
tcx.item_name(impl_param_def_id)
1384+
),
1385+
);
1386+
}
1387+
LateEarlyMismatch::LateInImpl(
1388+
impl_param_def_id,
1389+
trait_param_def_id,
1390+
early_bound_region,
1391+
) => {
1392+
let mut multispan = MultiSpan::from_spans(vec![
1393+
tcx.def_span(impl_param_def_id),
1394+
tcx.def_span(trait_param_def_id),
1395+
]);
1396+
multispan
1397+
.push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl...");
1398+
multispan
1399+
.push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait...");
1400+
multispan.push_span_label(
1401+
tcx.def_span(impl_param_def_id),
1402+
format!("`{}` is late-bound", tcx.item_name(impl_param_def_id)),
1403+
);
1404+
multispan.push_span_label(
1405+
tcx.def_span(trait_param_def_id),
1406+
format!("`{}` is early-bound", tcx.item_name(trait_param_def_id)),
1407+
);
1408+
if let Some(span) =
1409+
find_region_in_predicates(tcx, trait_m.def_id, early_bound_region)
1410+
{
1411+
multispan.push_span_label(
1412+
span,
1413+
format!(
1414+
"this lifetime bound makes `{}` early-bound",
1415+
tcx.item_name(trait_param_def_id)
1416+
),
1417+
);
1418+
}
1419+
diag.span_note(
1420+
multispan,
1421+
format!(
1422+
"`{}` differs between the trait and impl",
1423+
tcx.item_name(impl_param_def_id)
1424+
),
1425+
);
1426+
}
1427+
}
1428+
}
1429+
1430+
Some(diag.emit())
1431+
}
1432+
1433+
fn find_region_in_predicates<'tcx>(
1434+
tcx: TyCtxt<'tcx>,
1435+
def_id: DefId,
1436+
early_bound_region: ty::Region<'tcx>,
1437+
) -> Option<Span> {
1438+
for (pred, span) in tcx.explicit_predicates_of(def_id).instantiate_identity(tcx) {
1439+
if pred.visit_with(&mut FindRegion(early_bound_region)).is_break() {
1440+
return Some(span);
1441+
}
1442+
}
1443+
1444+
struct FindRegion<'tcx>(ty::Region<'tcx>);
1445+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindRegion<'tcx> {
1446+
type Result = ControlFlow<()>;
1447+
fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
1448+
if r == self.0 { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
1449+
}
1450+
}
1451+
1452+
None
1453+
}
1454+
12241455
#[instrument(level = "debug", skip(infcx))]
12251456
fn extract_spans_for_error_reporting<'tcx>(
12261457
infcx: &infer::InferCtxt<'tcx>,

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

Copy file name to clipboardExpand all lines: tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs
+3-3Lines 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
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
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
4444
}
4545

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

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.