diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 5dbd26694c5..5c275279579 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1008,6 +1008,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.get_impl_data(id).constness } + fn get_trait_item_def_id(&self, id: DefIndex) -> Option<DefId> { + self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode(self)) + } + fn get_coerce_unsized_info(&self, id: DefIndex) -> Option<ty::adjustment::CoerceUnsizedInfo> { self.get_impl_data(id).coerce_unsized_info } @@ -1289,6 +1293,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { vis: self.get_visibility(id), defaultness: container.defaultness(), def_id: self.local_def_id(id), + trait_item_def_id: self.get_trait_item_def_id(id), container: container.with_def_id(parent), fn_has_self_parameter: has_self, } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 12d66f4fc45..11a986f0a7c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1294,6 +1294,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } self.encode_ident_span(def_id, impl_item.ident); self.encode_item_type(def_id); + if let Some(trait_item_def_id) = impl_item.trait_item_def_id { + record!(self.tables.trait_item_def_id[def_id] <- trait_item_def_id); + } if impl_item.kind == ty::AssocKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4076e0b9e0f..fa44cbc2d55 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -302,6 +302,7 @@ define_tables! { ty: Table<DefIndex, Lazy!(Ty<'tcx>)>, fn_sig: Table<DefIndex, Lazy!(ty::PolyFnSig<'tcx>)>, impl_trait_ref: Table<DefIndex, Lazy!(ty::TraitRef<'tcx>)>, + trait_item_def_id: Table<DefIndex, Lazy<DefId>>, inherent_impls: Table<DefIndex, Lazy<[DefIndex]>>, variances: Table<DefIndex, Lazy<[ty::Variance]>>, generics: Table<DefIndex, Lazy<ty::Generics>>, diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index bf5a3e68250..5af4eef40d4 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -40,6 +40,7 @@ impl AssocItemContainer { } } +/// Information about an associated item #[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash)] pub struct AssocItem { pub def_id: DefId, @@ -50,6 +51,10 @@ pub struct AssocItem { pub defaultness: hir::Defaultness, pub container: AssocItemContainer, + /// If this is an item in an impl of a trait then this is the `DefId` of + /// the associated item on the trait that this implements. + pub trait_item_def_id: Option<DefId>, + /// Whether this is a method with an explicit self /// as its first parameter, allowing method calls. pub fn_has_self_parameter: bool, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 5f19991f9c7..c136411df27 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -794,19 +794,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - if let Res::Def(DefKind::Trait, trait_did) = t.path.res { - for impl_item_ref in items { - let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); - let trait_item_def_id = self - .tcx - .associated_items(trait_did) - .filter_by_name_unhygienic(impl_item.ident.name) - .next() - .map(|item| item.def_id); - if let Some(def_id) = trait_item_def_id { - // Pass `None` to skip deprecation warnings. - self.tcx.check_stability(def_id, None, impl_item.span, None); - } + for impl_item_ref in items { + let impl_item = self.tcx.associated_item(impl_item_ref.id.def_id); + + if let Some(def_id) = impl_item.trait_item_def_id { + // Pass `None` to skip deprecation warnings. + self.tcx.check_stability(def_id, None, impl_item_ref.span, None); } } } diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 7ec619e07ff..a83f0230814 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -710,13 +710,11 @@ impl<'tcx> SaveContext<'tcx> { } Res::Def(HirDefKind::AssocFn, decl_id) => { let def_id = if decl_id.is_local() { - let ti = self.tcx.associated_item(decl_id); - - self.tcx - .associated_items(ti.container.id()) - .filter_by_name_unhygienic(ti.ident.name) - .find(|item| item.defaultness.has_value()) - .map(|item| item.def_id) + if self.tcx.associated_item(decl_id).defaultness.has_value() { + Some(decl_id) + } else { + None + } } else { None }; diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index c38680651af..3f51442277f 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -436,23 +436,13 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t ) -> Arc<chalk_solve::rust_ir::AssociatedTyValue<RustInterner<'tcx>>> { let def_id = associated_ty_id.0; let assoc_item = self.interner.tcx.associated_item(def_id); - let (impl_id, trait_id) = match assoc_item.container { - AssocItemContainer::TraitContainer(def_id) => (def_id, def_id), - AssocItemContainer::ImplContainer(def_id) => { - (def_id, self.interner.tcx.impl_trait_ref(def_id).unwrap().def_id) - } - }; + let impl_id = assoc_item.container.id(); match assoc_item.kind { AssocKind::Type => {} _ => unimplemented!("Not possible??"), } - let trait_item = self - .interner - .tcx - .associated_items(trait_id) - .find_by_name_and_kind(self.interner.tcx, assoc_item.ident, assoc_item.kind, trait_id) - .unwrap(); + let trait_item_id = assoc_item.trait_item_def_id.expect("assoc_ty with no trait version"); let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(self.interner, bound_vars); let ty = self @@ -464,7 +454,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t Arc::new(chalk_solve::rust_ir::AssociatedTyValue { impl_id: chalk_ir::ImplId(impl_id), - associated_ty_id: chalk_ir::AssocTypeId(trait_item.def_id), + associated_ty_id: chalk_ir::AssocTypeId(trait_item_id), value: chalk_ir::Binders::new( binders, chalk_solve::rust_ir::AssociatedTyValueBound { ty }, diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 6c2657bd64b..49336abf3c4 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -1,8 +1,11 @@ use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{ + self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, TypeFoldable, +}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits; @@ -89,6 +92,7 @@ fn associated_item_from_trait_item_ref( vis: tcx.visibility(def_id), defaultness: trait_item_ref.defaultness, def_id: def_id.to_def_id(), + trait_item_def_id: Some(def_id.to_def_id()), container: ty::TraitContainer(parent_def_id.to_def_id()), fn_has_self_parameter: has_self, } @@ -106,17 +110,119 @@ fn associated_item_from_impl_item_ref( hir::AssocItemKind::Type => (ty::AssocKind::Type, false), }; + let trait_item_def_id = impl_item_base_id(tcx, parent_def_id, impl_item_ref); + ty::AssocItem { ident: impl_item_ref.ident, kind, vis: tcx.visibility(def_id), defaultness: impl_item_ref.defaultness, def_id: def_id.to_def_id(), + trait_item_def_id, container: ty::ImplContainer(parent_def_id.to_def_id()), fn_has_self_parameter: has_self, } } +fn impl_item_base_id<'tcx>( + tcx: TyCtxt<'tcx>, + parent_def_id: LocalDefId, + impl_item: &hir::ImplItemRef, +) -> Option<DefId> { + let impl_trait_ref = tcx.impl_trait_ref(parent_def_id)?; + + // If the trait reference itself is erroneous (so the compilation is going + // to fail), skip checking the items here -- the `impl_item` table in `tcx` + // isn't populated for such impls. + if impl_trait_ref.references_error() { + return None; + } + + // Locate trait items + let associated_items = tcx.associated_items(impl_trait_ref.def_id); + + // Match item against trait + let mut items = associated_items.filter_by_name(tcx, impl_item.ident, impl_trait_ref.def_id); + + let mut trait_item = items.next()?; + + let is_compatible = |ty: &&ty::AssocItem| match (ty.kind, &impl_item.kind) { + (ty::AssocKind::Const, hir::AssocItemKind::Const) => true, + (ty::AssocKind::Fn, hir::AssocItemKind::Fn { .. }) => true, + (ty::AssocKind::Type, hir::AssocItemKind::Type) => true, + _ => false, + }; + + // If we don't have a compatible item, we'll use the first one whose name matches + // to report an error. + let mut compatible_kind = is_compatible(&trait_item); + + if !compatible_kind { + if let Some(ty_trait_item) = items.find(is_compatible) { + compatible_kind = true; + trait_item = ty_trait_item; + } + } + + if compatible_kind { + Some(trait_item.def_id) + } else { + report_mismatch_error(tcx, trait_item.def_id, impl_trait_ref, impl_item); + None + } +} + +#[inline(never)] +#[cold] +fn report_mismatch_error<'tcx>( + tcx: TyCtxt<'tcx>, + trait_item_def_id: DefId, + impl_trait_ref: ty::TraitRef<'tcx>, + impl_item: &hir::ImplItemRef, +) { + let mut err = match impl_item.kind { + hir::AssocItemKind::Const => { + // Find associated const definition. + struct_span_err!( + tcx.sess, + impl_item.span, + E0323, + "item `{}` is an associated const, which doesn't match its trait `{}`", + impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + + hir::AssocItemKind::Fn { .. } => { + struct_span_err!( + tcx.sess, + impl_item.span, + E0324, + "item `{}` is an associated method, which doesn't match its trait `{}`", + impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + + hir::AssocItemKind::Type => { + struct_span_err!( + tcx.sess, + impl_item.span, + E0325, + "item `{}` is an associated type, which doesn't match its trait `{}`", + impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + }; + + err.span_label(impl_item.span, "does not match trait"); + if let Some(trait_span) = tcx.hir().span_if_local(trait_item_def_id) { + err.span_label(trait_span, "item in trait"); + } + err.emit(); +} + fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let parent_id = tcx.hir().get_parent_item(id); diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index fd7b3a55dfb..6f09dfd56dd 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -841,14 +841,8 @@ pub(super) fn check_specialization_validity<'tcx>( trait_def: &ty::TraitDef, trait_item: &ty::AssocItem, impl_id: DefId, - impl_item: &hir::ImplItem<'_>, + impl_item: &hir::ImplItemRef, ) { - let kind = match impl_item.kind { - hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, - }; - let ancestors = match trait_def.ancestors(tcx, impl_id) { Ok(ancestors) => ancestors, Err(_) => return, @@ -857,7 +851,7 @@ pub(super) fn check_specialization_validity<'tcx>( if parent.is_from_trait() { None } else { - Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) + Some((parent, parent.item(tcx, trait_item.def_id))) } }); @@ -894,7 +888,7 @@ pub(super) fn check_specialization_validity<'tcx>( } } -pub(super) fn check_impl_items_against_trait<'tcx>( +fn check_impl_items_against_trait<'tcx>( tcx: TyCtxt<'tcx>, full_impl_span: Span, impl_id: LocalDefId, @@ -926,174 +920,81 @@ pub(super) fn check_impl_items_against_trait<'tcx>( } } - // Locate trait definition and items let trait_def = tcx.trait_def(impl_trait_ref.def_id); - let impl_items = impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id)); - let associated_items = tcx.associated_items(impl_trait_ref.def_id); - // Check existing impl methods to see if they are both present in trait - // and compatible with trait signature - for impl_item in impl_items { - let ty_impl_item = tcx.associated_item(impl_item.def_id); - - let mut items = - associated_items.filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id); - - let (compatible_kind, ty_trait_item) = if let Some(ty_trait_item) = items.next() { - let is_compatible = |ty: &&ty::AssocItem| match (ty.kind, &impl_item.kind) { - (ty::AssocKind::Const, hir::ImplItemKind::Const(..)) => true, - (ty::AssocKind::Fn, hir::ImplItemKind::Fn(..)) => true, - (ty::AssocKind::Type, hir::ImplItemKind::TyAlias(..)) => true, - _ => false, - }; - - // If we don't have a compatible item, we'll use the first one whose name matches - // to report an error. - let mut compatible_kind = is_compatible(&ty_trait_item); - let mut trait_item = ty_trait_item; - - if !compatible_kind { - if let Some(ty_trait_item) = items.find(is_compatible) { - compatible_kind = true; - trait_item = ty_trait_item; - } - } - - (compatible_kind, trait_item) + for impl_item in impl_item_refs { + let ty_impl_item = tcx.associated_item(impl_item.id.def_id); + let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id { + tcx.associated_item(trait_item_id) } else { + // Checked in `associated_item`. + tcx.sess.delay_span_bug(impl_item.span, "missing associated item in trait"); continue; }; - - if compatible_kind { - match impl_item.kind { - hir::ImplItemKind::Const(..) => { - // Find associated const definition. - compare_const_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - ); - } - hir::ImplItemKind::Fn(..) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - compare_impl_method( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } - hir::ImplItemKind::TyAlias(impl_ty) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - compare_ty_impl( - tcx, - &ty_impl_item, - impl_ty.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } + match impl_item.kind { + hir::AssocItemKind::Const => { + // Find associated const definition. + compare_const_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + ); + } + hir::AssocItemKind::Fn { .. } => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + compare_impl_method( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } + hir::AssocItemKind::Type => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + compare_ty_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); } - - check_specialization_validity( - tcx, - trait_def, - &ty_trait_item, - impl_id.to_def_id(), - impl_item, - ); - } else { - report_mismatch_error( - tcx, - ty_trait_item.def_id, - impl_trait_ref, - impl_item, - &ty_impl_item, - ); } + + check_specialization_validity( + tcx, + trait_def, + &ty_trait_item, + impl_id.to_def_id(), + impl_item, + ); } if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { - let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); - // Check for missing items from trait let mut missing_items = Vec::new(); - for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { + for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) { let is_implemented = ancestors - .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.defining_node.is_from_trait()) - .unwrap_or(false); + .leaf_def(tcx, trait_item_id) + .map_or(false, |node_item| node_item.item.defaultness.has_value()); if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { - if !trait_item.defaultness.has_value() { - missing_items.push(*trait_item); - } + missing_items.push(tcx.associated_item(trait_item_id)); } } if !missing_items.is_empty() { + let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); missing_items_err(tcx, impl_span, &missing_items, full_impl_span); } } } -#[inline(never)] -#[cold] -fn report_mismatch_error<'tcx>( - tcx: TyCtxt<'tcx>, - trait_item_def_id: DefId, - impl_trait_ref: ty::TraitRef<'tcx>, - impl_item: &hir::ImplItem<'_>, - ty_impl_item: &ty::AssocItem, -) { - let mut err = match impl_item.kind { - hir::ImplItemKind::Const(..) => { - // Find associated const definition. - struct_span_err!( - tcx.sess, - impl_item.span, - E0323, - "item `{}` is an associated const, which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ) - } - - hir::ImplItemKind::Fn(..) => { - struct_span_err!( - tcx.sess, - impl_item.span, - E0324, - "item `{}` is an associated method, which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ) - } - - hir::ImplItemKind::TyAlias(_) => { - struct_span_err!( - tcx.sess, - impl_item.span, - E0325, - "item `{}` is an associated type, which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ) - } - }; - - err.span_label(impl_item.span, "does not match trait"); - if let Some(trait_span) = tcx.hir().span_if_local(trait_item_def_id) { - err.span_label(trait_span, "item in trait"); - } - err.emit(); -} - /// Checks whether a type can be represented in memory. In particular, it /// identifies types that contain themselves without indirection through a /// pointer, which would mean their size is unbounded. diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index a9e6b1caff0..d576154ff90 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -566,7 +566,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S fn report_forbidden_specialization( tcx: TyCtxt<'_>, - impl_item: &hir::ImplItem<'_>, + impl_item: &hir::ImplItemRef, parent_impl: DefId, ) { let mut err = struct_span_err!( @@ -598,7 +598,7 @@ fn report_forbidden_specialization( fn missing_items_err( tcx: TyCtxt<'_>, impl_span: Span, - missing_items: &[ty::AssocItem], + missing_items: &[&ty::AssocItem], full_impl_span: Span, ) { let missing_items_msg = missing_items diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 41c8a37a71a..d4d4baa3f71 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -3150,21 +3150,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { if let Some(impl_item) = tcx.opt_associated_item(def_id) { - if let ty::AssocItemContainer::ImplContainer(impl_def_id) = impl_item.container { - if let Some(trait_def_id) = tcx.trait_id_of_impl(impl_def_id) { - if let Some(trait_item) = tcx - .associated_items(trait_def_id) - .filter_by_name_unhygienic(impl_item.ident.name) - .find(move |trait_item| { - trait_item.kind == ty::AssocKind::Fn - && tcx.hygienic_eq(impl_item.ident, trait_item.ident, trait_def_id) - }) - { - return tcx - .codegen_fn_attrs(trait_item.def_id) - .flags - .intersects(CodegenFnAttrFlags::TRACK_CALLER); - } + if let ty::AssocItemContainer::ImplContainer(_) = impl_item.container { + if let Some(trait_item) = impl_item.trait_item_def_id { + return tcx + .codegen_fn_attrs(trait_item) + .flags + .intersects(CodegenFnAttrFlags::TRACK_CALLER); } } }