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 a810f8a

Browse filesBrowse 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
Copy full SHA for a810f8a

File tree

Expand file treeCollapse file tree

6 files changed

+463
-80
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+463
-80
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
+299-45Lines changed: 299 additions & 45 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;
@@ -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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.