Skip to content

Commit d2b22cf

Browse files
Better error message for late/early lifetime param mismatch
1 parent 464a737 commit d2b22cf

File tree

6 files changed

+398
-49
lines changed

6 files changed

+398
-49
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+234-3
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

+3-3
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)