Skip to content

Commit 1be6e2d

Browse files
committed
Link impl items to corresponding trait items in late resolver.
1 parent 488acf8 commit 1be6e2d

File tree

9 files changed

+111
-159
lines changed

9 files changed

+111
-159
lines changed

compiler/rustc_ast_lowering/src/index.rs

Lines changed: 2 additions & 1 deletion
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,6 +2885,8 @@ pub struct ImplItemRef {
28852885
pub kind: AssocItemKind,
28862886
pub span: Span,
28872887
pub defaultness: Defaultness,
2888+
/// When we are in a trait impl, link to the trait-item's id.
2889+
pub trait_item_def_id: Option<DefId>,
28882890
}
28892891

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

compiler/rustc_hir/src/intravisit.rs

Lines changed: 2 additions & 1 deletion
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

Lines changed: 0 additions & 15 deletions
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/late.rs

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13171317
// If this is a trait impl, ensure the const
13181318
// exists in trait
13191319
this.check_trait_item(
1320+
item.id,
13201321
item.ident,
13211322
&item.kind,
13221323
ValueNS,
@@ -1352,6 +1353,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13521353
// If this is a trait impl, ensure the method
13531354
// exists in trait
13541355
this.check_trait_item(
1356+
item.id,
13551357
item.ident,
13561358
&item.kind,
13571359
ValueNS,
@@ -1379,6 +1381,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13791381
// If this is a trait impl, ensure the type
13801382
// exists in trait
13811383
this.check_trait_item(
1384+
item.id,
13821385
item.ident,
13831386
&item.kind,
13841387
TypeNS,
@@ -1409,6 +1412,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
14091412

14101413
fn check_trait_item<F>(
14111414
&mut self,
1415+
id: NodeId,
14121416
ident: Ident,
14131417
kind: &AssocItemKind,
14141418
ns: Namespace,
@@ -1417,26 +1421,94 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
14171421
) where
14181422
F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
14191423
{
1420-
// If there is a TraitRef in scope for an impl, then the method must be in the
1421-
// trait.
1422-
if let Some((module, _)) = self.current_trait_ref {
1423-
if self
1424-
.r
1425-
.resolve_ident_in_module(
1426-
ModuleOrUniformRoot::Module(module),
1424+
// If there is a TraitRef in scope for an impl, then the method must be in the trait.
1425+
let Some((module, _)) = &self.current_trait_ref else { return; };
1426+
let mut binding = self.r.resolve_ident_in_module(
1427+
ModuleOrUniformRoot::Module(module),
1428+
ident,
1429+
ns,
1430+
&self.parent_scope,
1431+
false,
1432+
span,
1433+
);
1434+
if binding.is_err() {
1435+
// We could not find the trait item in the correct namespace.
1436+
// Check the other namespace to report an error.
1437+
let ns = match ns {
1438+
ValueNS => TypeNS,
1439+
TypeNS => ValueNS,
1440+
_ => ns,
1441+
};
1442+
binding = self.r.resolve_ident_in_module(
1443+
ModuleOrUniformRoot::Module(module),
1444+
ident,
1445+
ns,
1446+
&self.parent_scope,
1447+
false,
1448+
span,
1449+
);
1450+
}
1451+
let Ok(binding) = binding else {
1452+
// We could not find the method: report an error.
1453+
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
1454+
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
1455+
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
1456+
return;
1457+
};
1458+
1459+
let res = binding.res();
1460+
let Res::Def(def_kind, _) = res else { bug!() };
1461+
match (def_kind, kind) {
1462+
(DefKind::AssocTy, AssocItemKind::TyAlias(..))
1463+
| (DefKind::AssocFn, AssocItemKind::Fn(..))
1464+
| (DefKind::AssocConst, AssocItemKind::Const(..)) => {
1465+
self.r.record_partial_res(id, PartialRes::new(res));
1466+
return;
1467+
}
1468+
_ => {}
1469+
}
1470+
1471+
// The method kind does not correspond to what appeared in the trait, report.
1472+
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
1473+
let path = &path_names_to_string(path);
1474+
let mut err = match kind {
1475+
AssocItemKind::Const(..) => {
1476+
rustc_errors::struct_span_err!(
1477+
self.r.session,
1478+
span,
1479+
E0323,
1480+
"item `{}` is an associated const, which doesn't match its trait `{}`",
14271481
ident,
1428-
ns,
1429-
&self.parent_scope,
1430-
false,
1482+
path,
1483+
)
1484+
}
1485+
AssocItemKind::Fn(..) => {
1486+
rustc_errors::struct_span_err!(
1487+
self.r.session,
14311488
span,
1489+
E0324,
1490+
"item `{}` is an associated method, which doesn't match its trait `{}`",
1491+
ident,
1492+
path,
14321493
)
1433-
.is_err()
1434-
{
1435-
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
1436-
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
1437-
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
14381494
}
1439-
}
1495+
AssocItemKind::TyAlias(..) => {
1496+
rustc_errors::struct_span_err!(
1497+
self.r.session,
1498+
span,
1499+
E0325,
1500+
"item `{}` is an associated type, which doesn't match its trait `{}`",
1501+
ident,
1502+
path,
1503+
)
1504+
}
1505+
AssocItemKind::MacCall(..) => {
1506+
span_bug!(span, "macros should have been expanded")
1507+
}
1508+
};
1509+
err.span_label(span, "does not match trait");
1510+
err.span_label(binding.span, "item in trait");
1511+
err.emit();
14401512
}
14411513

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

compiler/rustc_ty_utils/src/assoc.rs

Lines changed: 2 additions & 104 deletions
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-
}

src/test/ui/span/impl-wrong-item-for-trait.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,10 @@ impl Foo for FooTypeForMethod {
2929
//~^ ERROR E0046
3030
type bar = u64;
3131
//~^ ERROR E0325
32-
//~| ERROR E0437
3332
const MY_CONST: u32 = 1;
3433
}
3534

36-
impl Debug for FooTypeForMethod {
37-
}
38-
//~^^ ERROR E0046
35+
impl Debug for FooTypeForMethod {}
36+
//~^ ERROR E0046
3937

40-
fn main () {}
38+
fn main() {}

src/test/ui/span/impl-wrong-item-for-trait.stderr

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
error[E0437]: type `bar` is not a member of trait `Foo`
2-
--> $DIR/impl-wrong-item-for-trait.rs:30:5
1+
error[E0323]: item `bar` is an associated const, which doesn't match its trait `Foo`
2+
--> $DIR/impl-wrong-item-for-trait.rs:12:5
33
|
4-
LL | type bar = u64;
5-
| ^^^^^^^^^^^^^^^ not a member of trait `Foo`
4+
LL | fn bar(&self);
5+
| -------------- item in trait
6+
...
7+
LL | const bar: u64 = 1;
8+
| ^^^^^^^^^^^^^^^^^^^ does not match trait
69

710
error[E0324]: item `MY_CONST` is an associated method, which doesn't match its trait `Foo`
811
--> $DIR/impl-wrong-item-for-trait.rs:22:5
@@ -13,15 +16,6 @@ LL | const MY_CONST: u32;
1316
LL | fn MY_CONST() {}
1417
| ^^^^^^^^^^^^^^^^ does not match trait
1518

16-
error[E0323]: item `bar` is an associated const, which doesn't match its trait `Foo`
17-
--> $DIR/impl-wrong-item-for-trait.rs:12:5
18-
|
19-
LL | fn bar(&self);
20-
| -------------- item in trait
21-
...
22-
LL | const bar: u64 = 1;
23-
| ^^^^^^^^^^^^^^^^^^^ does not match trait
24-
2519
error[E0325]: item `bar` is an associated type, which doesn't match its trait `Foo`
2620
--> $DIR/impl-wrong-item-for-trait.rs:30:5
2721
|
@@ -59,14 +53,14 @@ LL | impl Foo for FooTypeForMethod {
5953
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `bar` in implementation
6054

6155
error[E0046]: not all trait items implemented, missing: `fmt`
62-
--> $DIR/impl-wrong-item-for-trait.rs:36:1
56+
--> $DIR/impl-wrong-item-for-trait.rs:35:1
6357
|
64-
LL | impl Debug for FooTypeForMethod {
58+
LL | impl Debug for FooTypeForMethod {}
6559
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation
6660
|
6761
= help: implement the missing item: `fn fmt(&self, _: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { todo!() }`
6862

69-
error: aborting due to 8 previous errors
63+
error: aborting due to 7 previous errors
7064

71-
Some errors have detailed explanations: E0046, E0323, E0324, E0325, E0437.
65+
Some errors have detailed explanations: E0046, E0323, E0324, E0325.
7266
For more information about an error, try `rustc --explain E0046`.

0 commit comments

Comments
 (0)