Skip to content

Commit ec4bcaa

Browse files
committed
Auto merge of #92441 - cjgillot:resolve-trait-impl-item, r=matthewjasper
Link impl items to corresponding trait items in late resolver. Hygienically linking trait impl items to declarations in the trait can be done directly by the late resolver. In fact, it is already done to diagnose unknown items. This PR uses this resolution work and stores the `DefId` of the trait item in the HIR. This avoids having to do this resolution manually later. r? `@matthewjasper` Related to #90639. The added `trait_item_id` field can be moved to `ImplItemRef` to be used directly by your PR.
2 parents b13a5bf + 441c1a6 commit ec4bcaa

26 files changed

+474
-511
lines changed

compiler/rustc_ast_lowering/src/index.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
335335
fn visit_impl_item_ref(&mut self, ii: &'hir ImplItemRef) {
336336
// Do not visit the duplicate information in ImplItemRef. We want to
337337
// map the actual nodes, not the duplicate ones in the *Ref.
338-
let ImplItemRef { id, ident: _, kind: _, span: _, defaultness: _ } = *ii;
338+
let ImplItemRef { id, ident: _, kind: _, span: _, defaultness: _, trait_item_def_id: _ } =
339+
*ii;
339340

340341
self.visit_nested_impl_item(id);
341342
}

compiler/rustc_ast_lowering/src/item.rs

+1
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
925925
}
926926
AssocItemKind::MacCall(..) => unimplemented!(),
927927
},
928+
trait_item_def_id: self.resolver.get_partial_res(i.id).map(|r| r.base_res().def_id()),
928929
}
929930
}
930931

compiler/rustc_hir/src/hir.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2881,6 +2881,8 @@ pub struct ImplItemRef {
28812881
pub kind: AssocItemKind,
28822882
pub span: Span,
28832883
pub defaultness: Defaultness,
2884+
/// When we are in a trait impl, link to the trait-item's id.
2885+
pub trait_item_def_id: Option<DefId>,
28842886
}
28852887

28862888
#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)]

compiler/rustc_hir/src/intravisit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,8 @@ pub fn walk_foreign_item_ref<'v, V: Visitor<'v>>(
10881088

10891089
pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, impl_item_ref: &'v ImplItemRef) {
10901090
// N.B., deliberately force a compilation error if/when new fields are added.
1091-
let ImplItemRef { id, ident, ref kind, span: _, ref defaultness } = *impl_item_ref;
1091+
let ImplItemRef { id, ident, ref kind, span: _, ref defaultness, trait_item_def_id: _ } =
1092+
*impl_item_ref;
10921093
visitor.visit_nested_impl_item(id);
10931094
visitor.visit_ident(ident);
10941095
visitor.visit_associated_item_kind(kind);

compiler/rustc_middle/src/ty/assoc.rs

-15
Original file line numberDiff line numberDiff line change
@@ -139,21 +139,6 @@ impl<'tcx> AssocItems<'tcx> {
139139
self.items.get_by_key(name).copied()
140140
}
141141

142-
/// Returns an iterator over all associated items with the given name.
143-
///
144-
/// Multiple items may have the same name if they are in different `Namespace`s. For example,
145-
/// an associated type can have the same name as a method. Use one of the `find_by_name_and_*`
146-
/// methods below if you know which item you are looking for.
147-
pub fn filter_by_name<'a>(
148-
&'a self,
149-
tcx: TyCtxt<'a>,
150-
ident: Ident,
151-
parent_def_id: DefId,
152-
) -> impl 'a + Iterator<Item = &'a ty::AssocItem> {
153-
self.filter_by_name_unhygienic(ident.name)
154-
.filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id))
155-
}
156-
157142
/// Returns the associated item with the given name and `AssocKind`, if one exists.
158143
pub fn find_by_name_and_kind(
159144
&self,

compiler/rustc_resolve/src/diagnostics.rs

+19
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,25 @@ impl<'a> Resolver<'a> {
602602

603603
err
604604
}
605+
ResolutionError::TraitImplMismatch {
606+
name,
607+
kind,
608+
code,
609+
trait_item_span,
610+
trait_path,
611+
} => {
612+
let mut err = self.session.struct_span_err_with_code(
613+
span,
614+
&format!(
615+
"item `{}` is an associated {}, which doesn't match its trait `{}`",
616+
name, kind, trait_path,
617+
),
618+
code,
619+
);
620+
err.span_label(span, "does not match trait");
621+
err.span_label(trait_item_span, "item in trait");
622+
err
623+
}
605624
}
606625
}
607626

compiler/rustc_resolve/src/late.rs

+62-23
Original file line numberDiff line numberDiff line change
@@ -1247,15 +1247,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
12471247
);
12481248
let res = res.base_res();
12491249
if res != Res::Err {
1250-
new_id = Some(res.def_id());
1251-
let span = trait_ref.path.span;
12521250
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(
12531251
&path,
12541252
Some(TypeNS),
1255-
false,
1256-
span,
1253+
true,
1254+
trait_ref.path.span,
12571255
CrateLint::SimplePath(trait_ref.ref_id),
12581256
) {
1257+
new_id = Some(res.def_id());
12591258
new_val = Some((module, trait_ref.clone()));
12601259
}
12611260
}
@@ -1324,6 +1323,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13241323
// If this is a trait impl, ensure the const
13251324
// exists in trait
13261325
this.check_trait_item(
1326+
item.id,
13271327
item.ident,
13281328
&item.kind,
13291329
ValueNS,
@@ -1359,6 +1359,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13591359
// If this is a trait impl, ensure the method
13601360
// exists in trait
13611361
this.check_trait_item(
1362+
item.id,
13621363
item.ident,
13631364
&item.kind,
13641365
ValueNS,
@@ -1386,6 +1387,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13861387
// If this is a trait impl, ensure the type
13871388
// exists in trait
13881389
this.check_trait_item(
1390+
item.id,
13891391
item.ident,
13901392
&item.kind,
13911393
TypeNS,
@@ -1416,34 +1418,71 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
14161418

14171419
fn check_trait_item<F>(
14181420
&mut self,
1419-
ident: Ident,
1421+
id: NodeId,
1422+
mut ident: Ident,
14201423
kind: &AssocItemKind,
14211424
ns: Namespace,
14221425
span: Span,
14231426
err: F,
14241427
) where
14251428
F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
14261429
{
1427-
// If there is a TraitRef in scope for an impl, then the method must be in the
1428-
// trait.
1429-
if let Some((module, _)) = self.current_trait_ref {
1430-
if self
1431-
.r
1432-
.resolve_ident_in_module(
1433-
ModuleOrUniformRoot::Module(module),
1434-
ident,
1435-
ns,
1436-
&self.parent_scope,
1437-
false,
1438-
span,
1439-
)
1440-
.is_err()
1441-
{
1442-
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
1443-
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
1444-
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
1430+
// If there is a TraitRef in scope for an impl, then the method must be in the trait.
1431+
let Some((module, _)) = &self.current_trait_ref else { return; };
1432+
ident.span.normalize_to_macros_2_0_and_adjust(module.expansion);
1433+
let key = self.r.new_key(ident, ns);
1434+
let mut binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
1435+
debug!(?binding);
1436+
if binding.is_none() {
1437+
// We could not find the trait item in the correct namespace.
1438+
// Check the other namespace to report an error.
1439+
let ns = match ns {
1440+
ValueNS => TypeNS,
1441+
TypeNS => ValueNS,
1442+
_ => ns,
1443+
};
1444+
let key = self.r.new_key(ident, ns);
1445+
binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
1446+
debug!(?binding);
1447+
}
1448+
let Some(binding) = binding else {
1449+
// We could not find the method: report an error.
1450+
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
1451+
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
1452+
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
1453+
return;
1454+
};
1455+
1456+
let res = binding.res();
1457+
let Res::Def(def_kind, _) = res else { bug!() };
1458+
match (def_kind, kind) {
1459+
(DefKind::AssocTy, AssocItemKind::TyAlias(..))
1460+
| (DefKind::AssocFn, AssocItemKind::Fn(..))
1461+
| (DefKind::AssocConst, AssocItemKind::Const(..)) => {
1462+
self.r.record_partial_res(id, PartialRes::new(res));
1463+
return;
14451464
}
1465+
_ => {}
14461466
}
1467+
1468+
// The method kind does not correspond to what appeared in the trait, report.
1469+
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
1470+
let (code, kind) = match kind {
1471+
AssocItemKind::Const(..) => (rustc_errors::error_code!(E0323), "const"),
1472+
AssocItemKind::Fn(..) => (rustc_errors::error_code!(E0324), "method"),
1473+
AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"),
1474+
AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"),
1475+
};
1476+
self.report_error(
1477+
span,
1478+
ResolutionError::TraitImplMismatch {
1479+
name: ident.name,
1480+
kind,
1481+
code,
1482+
trait_path: path_names_to_string(path),
1483+
trait_item_span: binding.span,
1484+
},
1485+
);
14471486
}
14481487

14491488
fn resolve_params(&mut self, params: &'ast [Param]) {

compiler/rustc_resolve/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,14 @@ enum ResolutionError<'a> {
262262
SelfInGenericParamDefault,
263263
/// Error E0767: use of unreachable label
264264
UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option<LabelSuggestion> },
265+
/// Error E0323, E0324, E0325: mismatch between trait item and impl item.
266+
TraitImplMismatch {
267+
name: Symbol,
268+
kind: &'static str,
269+
trait_path: String,
270+
trait_item_span: Span,
271+
code: rustc_errors::DiagnosticId,
272+
},
265273
}
266274

267275
enum VisResolutionError<'a> {

compiler/rustc_ty_utils/src/assoc.rs

+2-104
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use rustc_data_structures::fx::FxHashMap;
2-
use rustc_errors::struct_span_err;
32
use rustc_hir as hir;
43
use rustc_hir::def_id::{DefId, LocalDefId};
5-
use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
4+
use rustc_middle::ty::{self, TyCtxt};
65

76
pub fn provide(providers: &mut ty::query::Providers) {
87
*providers = ty::query::Providers {
@@ -125,115 +124,14 @@ fn associated_item_from_impl_item_ref(
125124
hir::AssocItemKind::Type => (ty::AssocKind::Type, false),
126125
};
127126

128-
let trait_item_def_id = impl_item_base_id(tcx, parent_def_id, impl_item_ref);
129-
130127
ty::AssocItem {
131128
ident: impl_item_ref.ident,
132129
kind,
133130
vis: tcx.visibility(def_id),
134131
defaultness: impl_item_ref.defaultness,
135132
def_id: def_id.to_def_id(),
136-
trait_item_def_id,
133+
trait_item_def_id: impl_item_ref.trait_item_def_id,
137134
container: ty::ImplContainer(parent_def_id.to_def_id()),
138135
fn_has_self_parameter: has_self,
139136
}
140137
}
141-
142-
fn impl_item_base_id<'tcx>(
143-
tcx: TyCtxt<'tcx>,
144-
parent_def_id: LocalDefId,
145-
impl_item: &hir::ImplItemRef,
146-
) -> Option<DefId> {
147-
let impl_trait_ref = tcx.impl_trait_ref(parent_def_id)?;
148-
149-
// If the trait reference itself is erroneous (so the compilation is going
150-
// to fail), skip checking the items here -- the `impl_item` table in `tcx`
151-
// isn't populated for such impls.
152-
if impl_trait_ref.references_error() {
153-
return None;
154-
}
155-
156-
// Locate trait items
157-
let associated_items = tcx.associated_items(impl_trait_ref.def_id);
158-
159-
// Match item against trait
160-
let mut items = associated_items.filter_by_name(tcx, impl_item.ident, impl_trait_ref.def_id);
161-
162-
let mut trait_item = items.next()?;
163-
164-
let is_compatible = |ty: &&ty::AssocItem| match (ty.kind, &impl_item.kind) {
165-
(ty::AssocKind::Const, hir::AssocItemKind::Const) => true,
166-
(ty::AssocKind::Fn, hir::AssocItemKind::Fn { .. }) => true,
167-
(ty::AssocKind::Type, hir::AssocItemKind::Type) => true,
168-
_ => false,
169-
};
170-
171-
// If we don't have a compatible item, we'll use the first one whose name matches
172-
// to report an error.
173-
let mut compatible_kind = is_compatible(&trait_item);
174-
175-
if !compatible_kind {
176-
if let Some(ty_trait_item) = items.find(is_compatible) {
177-
compatible_kind = true;
178-
trait_item = ty_trait_item;
179-
}
180-
}
181-
182-
if compatible_kind {
183-
Some(trait_item.def_id)
184-
} else {
185-
report_mismatch_error(tcx, trait_item.def_id, impl_trait_ref, impl_item);
186-
None
187-
}
188-
}
189-
190-
#[inline(never)]
191-
#[cold]
192-
fn report_mismatch_error<'tcx>(
193-
tcx: TyCtxt<'tcx>,
194-
trait_item_def_id: DefId,
195-
impl_trait_ref: ty::TraitRef<'tcx>,
196-
impl_item: &hir::ImplItemRef,
197-
) {
198-
let mut err = match impl_item.kind {
199-
hir::AssocItemKind::Const => {
200-
// Find associated const definition.
201-
struct_span_err!(
202-
tcx.sess,
203-
impl_item.span,
204-
E0323,
205-
"item `{}` is an associated const, which doesn't match its trait `{}`",
206-
impl_item.ident,
207-
impl_trait_ref.print_only_trait_path()
208-
)
209-
}
210-
211-
hir::AssocItemKind::Fn { .. } => {
212-
struct_span_err!(
213-
tcx.sess,
214-
impl_item.span,
215-
E0324,
216-
"item `{}` is an associated method, which doesn't match its trait `{}`",
217-
impl_item.ident,
218-
impl_trait_ref.print_only_trait_path()
219-
)
220-
}
221-
222-
hir::AssocItemKind::Type => {
223-
struct_span_err!(
224-
tcx.sess,
225-
impl_item.span,
226-
E0325,
227-
"item `{}` is an associated type, which doesn't match its trait `{}`",
228-
impl_item.ident,
229-
impl_trait_ref.print_only_trait_path()
230-
)
231-
}
232-
};
233-
234-
err.span_label(impl_item.span, "does not match trait");
235-
if let Some(trait_span) = tcx.hir().span_if_local(trait_item_def_id) {
236-
err.span_label(trait_span, "item in trait");
237-
}
238-
err.emit();
239-
}

0 commit comments

Comments
 (0)