Skip to content

Commit a810f8a

Browse files
authored
Rollup merge of #140523 - compiler-errors:late-early-mismatch, r=jackh726
Better error message for late/early lifetime param mismatch Rework the way we report early-/late-bound lifetime param mismatches to equate the trait and impl signatures using region variables, so that we can detect when a late-bound param is present in the signature in place of an early-bound param, or vice versa. The diagnostic is a bit more technical, but it's more obviously clear to see what the problem is, even if it's not great at explaining how to fix it. I think this could be improved further, but I still think it's much better than what exists today. Note to reviewer(s): I'd appreciate if we didn't bikeshed *too* much about this verbiage, b/c I hope it's clear that the old message sucked a lot. I'm happy to file bugs for interested new contributors to improve the messaging further. Edit(fmease): Fixes #33624.
2 parents 8a3ab85 + f03d246 commit a810f8a

File tree

6 files changed

+463
-80
lines changed

6 files changed

+463
-80
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+299-45
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;
@@ -1137,65 +1137,319 @@ fn check_region_bounds_on_impl_item<'tcx>(
11371137
// but found 0" it's confusing, because it looks like there
11381138
// are zero. Since I don't quite know how to phrase things at
11391139
// the moment, give a kind of vague error message.
1140-
if trait_params != impl_params {
1141-
let span = tcx
1142-
.hir_get_generics(impl_m.def_id.expect_local())
1143-
.expect("expected impl item to have generics or else we can't compare them")
1144-
.span;
1145-
1146-
let mut generics_span = None;
1147-
let mut bounds_span = vec![];
1148-
let mut where_span = None;
1149-
if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id)
1150-
&& let Some(trait_generics) = trait_node.generics()
1151-
{
1152-
generics_span = Some(trait_generics.span);
1153-
// FIXME: we could potentially look at the impl's bounds to not point at bounds that
1154-
// *are* present in the impl.
1155-
for p in trait_generics.predicates {
1156-
if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind {
1157-
for b in pred.bounds {
1140+
if trait_params == impl_params {
1141+
return Ok(());
1142+
}
1143+
1144+
if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) {
1145+
return Err(guar);
1146+
}
1147+
1148+
let span = tcx
1149+
.hir_get_generics(impl_m.def_id.expect_local())
1150+
.expect("expected impl item to have generics or else we can't compare them")
1151+
.span;
1152+
1153+
let mut generics_span = None;
1154+
let mut bounds_span = vec![];
1155+
let mut where_span = None;
1156+
1157+
if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id)
1158+
&& let Some(trait_generics) = trait_node.generics()
1159+
{
1160+
generics_span = Some(trait_generics.span);
1161+
// FIXME: we could potentially look at the impl's bounds to not point at bounds that
1162+
// *are* present in the impl.
1163+
for p in trait_generics.predicates {
1164+
match p.kind {
1165+
hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
1166+
bounds,
1167+
..
1168+
})
1169+
| hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
1170+
bounds,
1171+
..
1172+
}) => {
1173+
for b in *bounds {
11581174
if let hir::GenericBound::Outlives(lt) = b {
11591175
bounds_span.push(lt.ident.span);
11601176
}
11611177
}
11621178
}
1179+
_ => {}
11631180
}
1164-
if let Some(impl_node) = tcx.hir_get_if_local(impl_m.def_id)
1165-
&& let Some(impl_generics) = impl_node.generics()
1166-
{
1167-
let mut impl_bounds = 0;
1168-
for p in impl_generics.predicates {
1169-
if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind {
1170-
for b in pred.bounds {
1181+
}
1182+
if let Some(impl_node) = tcx.hir_get_if_local(impl_m.def_id)
1183+
&& let Some(impl_generics) = impl_node.generics()
1184+
{
1185+
let mut impl_bounds = 0;
1186+
for p in impl_generics.predicates {
1187+
match p.kind {
1188+
hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
1189+
bounds,
1190+
..
1191+
})
1192+
| hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
1193+
bounds,
1194+
..
1195+
}) => {
1196+
for b in *bounds {
11711197
if let hir::GenericBound::Outlives(_) = b {
11721198
impl_bounds += 1;
11731199
}
11741200
}
11751201
}
1202+
_ => {}
1203+
}
1204+
}
1205+
if impl_bounds == bounds_span.len() {
1206+
bounds_span = vec![];
1207+
} else if impl_generics.has_where_clause_predicates {
1208+
where_span = Some(impl_generics.where_clause_span);
1209+
}
1210+
}
1211+
}
1212+
1213+
let reported = tcx
1214+
.dcx()
1215+
.create_err(LifetimesOrBoundsMismatchOnTrait {
1216+
span,
1217+
item_kind: impl_m.descr(),
1218+
ident: impl_m.ident(tcx),
1219+
generics_span,
1220+
bounds_span,
1221+
where_span,
1222+
})
1223+
.emit_unless(delay);
1224+
1225+
Err(reported)
1226+
}
1227+
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+
);
11761378
}
1177-
if impl_bounds == bounds_span.len() {
1178-
bounds_span = vec![];
1179-
} else if impl_generics.has_where_clause_predicates {
1180-
where_span = Some(impl_generics.where_clause_span);
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+
);
11811418
}
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+
);
11821426
}
11831427
}
1184-
let reported = tcx
1185-
.dcx()
1186-
.create_err(LifetimesOrBoundsMismatchOnTrait {
1187-
span,
1188-
item_kind: impl_m.descr(),
1189-
ident: impl_m.ident(tcx),
1190-
generics_span,
1191-
bounds_span,
1192-
where_span,
1193-
})
1194-
.emit_unless(delay);
1195-
return Err(reported);
11961428
}
11971429

1198-
Ok(())
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
11991453
}
12001454

12011455
#[instrument(level = "debug", skip(infcx))]

0 commit comments

Comments
 (0)