From 49ba323c8db6fd5fbe3d72f623c9d89cb09c508d Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sun, 29 Mar 2020 00:57:49 +0100 Subject: [PATCH] spec. graph: track defining and finalizing impls --- src/librustc_middle/traits/mod.rs | 1 + .../traits/specialization_graph.rs | 62 +++++++++++++++++-- src/librustc_middle/traits/util.rs | 17 +++++ src/librustc_trait_selection/traits/mod.rs | 3 +- .../traits/project.rs | 48 ++++---------- .../traits/specialize/mod.rs | 2 +- src/librustc_trait_selection/traits/util.rs | 16 +---- src/librustc_typeck/check/mod.rs | 7 ++- 8 files changed, 93 insertions(+), 63 deletions(-) create mode 100644 src/librustc_middle/traits/util.rs diff --git a/src/librustc_middle/traits/mod.rs b/src/librustc_middle/traits/mod.rs index c129b574fd3..d47e2d75fba 100644 --- a/src/librustc_middle/traits/mod.rs +++ b/src/librustc_middle/traits/mod.rs @@ -6,6 +6,7 @@ pub mod query; pub mod select; pub mod specialization_graph; mod structural_impls; +pub mod util; use crate::mir::interpret::ErrorHandled; use crate::ty::subst::SubstsRef; diff --git a/src/librustc_middle/traits/specialization_graph.rs b/src/librustc_middle/traits/specialization_graph.rs index b09f1e66f17..f66f94fe8c1 100644 --- a/src/librustc_middle/traits/specialization_graph.rs +++ b/src/librustc_middle/traits/specialization_graph.rs @@ -154,9 +154,45 @@ impl Iterator for Ancestors<'_> { } } -pub struct NodeItem { - pub node: Node, +/// Information about the most specialized definition of an associated item. +pub struct LeafDef { + /// The associated item described by this `LeafDef`. pub item: ty::AssocItem, + + /// The node in the specialization graph containing the definition of `item`. + pub defining_node: Node, + + /// The "top-most" (ie. least specialized) specialization graph node that finalized the + /// definition of `item`. + /// + /// Example: + /// + /// ``` + /// trait Tr { + /// fn assoc(&self); + /// } + /// + /// impl Tr for T { + /// default fn assoc(&self) {} + /// } + /// + /// impl Tr for u8 {} + /// ``` + /// + /// If we start the leaf definition search at `impl Tr for u8`, that impl will be the + /// `finalizing_node`, while `defining_node` will be the generic impl. + /// + /// If the leaf definition search is started at the generic impl, `finalizing_node` will be + /// `None`, since the most specialized impl we found still allows overriding the method + /// (doesn't finalize it). + pub finalizing_node: Option, +} + +impl LeafDef { + /// Returns whether this definition is known to not be further specializable. + pub fn is_final(&self) -> bool { + self.finalizing_node.is_some() + } } impl<'tcx> Ancestors<'tcx> { @@ -167,11 +203,27 @@ impl<'tcx> Ancestors<'tcx> { tcx: TyCtxt<'tcx>, trait_item_name: Ident, trait_item_kind: ty::AssocKind, - ) -> Option { + ) -> Option { let trait_def_id = self.trait_def_id; + let mut finalizing_node = None; + self.find_map(|node| { - node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) - .map(|item| NodeItem { node, item }) + if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) { + if finalizing_node.is_none() { + let is_specializable = item.defaultness.is_default() + || super::util::impl_is_default(tcx, node.def_id()); + + if !is_specializable { + finalizing_node = Some(node); + } + } + + Some(LeafDef { item, defining_node: node, finalizing_node }) + } else { + // Item not mentioned. This "finalizes" any defaulted item provided by an ancestor. + finalizing_node = Some(node); + None + } }) } } diff --git a/src/librustc_middle/traits/util.rs b/src/librustc_middle/traits/util.rs new file mode 100644 index 00000000000..cb29cf0760e --- /dev/null +++ b/src/librustc_middle/traits/util.rs @@ -0,0 +1,17 @@ +use crate::ty::TyCtxt; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; + +pub fn impl_is_default(tcx: TyCtxt<'_>, node_item_def_id: DefId) -> bool { + match tcx.hir().as_local_hir_id(node_item_def_id) { + Some(hir_id) => { + let item = tcx.hir().expect_item(hir_id); + if let hir::ItemKind::Impl { defaultness, .. } = item.kind { + defaultness.is_default() + } else { + false + } + } + None => tcx.impl_defaultness(node_item_def_id).is_default(), + } +} diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index 3ef44a19841..d6204975a7b 100644 --- a/src/librustc_trait_selection/traits/mod.rs +++ b/src/librustc_trait_selection/traits/mod.rs @@ -64,8 +64,7 @@ pub use self::structural_match::NonStructuralMatchTy; pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs}; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; pub use self::util::{ - get_vtable_index_of_object_method, impl_is_default, impl_item_is_final, - predicate_for_trait_def, upcast_choices, + get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, }; pub use self::util::{ supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits, diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs index 1e26b62c759..cd89111a177 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/src/librustc_trait_selection/traits/project.rs @@ -1015,49 +1015,21 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id) .map_err(|ErrorReported| ())?; - let is_default = if node_item.node.is_from_trait() { - // If true, the impl inherited a `type Foo = Bar` - // given in the trait, which is implicitly default. - // Otherwise, the impl did not specify `type` and - // neither did the trait: - // - // ```rust - // trait Foo { type T; } - // impl Foo for Bar { } - // ``` - // - // This is an error, but it will be - // reported in `check_impl_items_against_trait`. - // We accept it here but will flag it as - // an error when we confirm the candidate - // (which will ultimately lead to `normalize_to_error` - // being invoked). - false - } else { - // If we're looking at a trait *impl*, the item is - // specializable if the impl or the item are marked - // `default`. - node_item.item.defaultness.is_default() - || super::util::impl_is_default(selcx.tcx(), node_item.node.def_id()) - }; - - match is_default { + if node_item.is_final() { // Non-specializable items are always projectable - false => true, - + true + } else { // Only reveal a specializable default if we're past type-checking // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - true if obligation.param_env.reveal == Reveal::All => { + if obligation.param_env.reveal == Reveal::All { // NOTE(eddyb) inference variables can resolve to parameters, so // assume `poly_trait_ref` isn't monomorphic, if it contains any. let poly_trait_ref = selcx.infcx().resolve_vars_if_possible(&poly_trait_ref); !poly_trait_ref.needs_infer() && !poly_trait_ref.needs_subst() - } - - true => { + } else { debug!( "assemble_candidates_from_impls: not eligible due to default: \ assoc_ty={} predicate={}", @@ -1422,7 +1394,8 @@ fn confirm_impl_candidate<'cx, 'tcx>( return Progress { ty: tcx.types.err, obligations: nested }; } let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs); - let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.node); + let substs = + translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node); let ty = if let ty::AssocKind::OpaqueTy = assoc_ty.item.kind { let item_substs = InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id); tcx.mk_opaque(assoc_ty.item.def_id, item_substs) @@ -1447,7 +1420,7 @@ fn assoc_ty_def( selcx: &SelectionContext<'_, '_>, impl_def_id: DefId, assoc_ty_def_id: DefId, -) -> Result { +) -> Result { let tcx = selcx.tcx(); let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident; let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; @@ -1464,9 +1437,10 @@ fn assoc_ty_def( if matches!(item.kind, ty::AssocKind::Type | ty::AssocKind::OpaqueTy) && tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id) { - return Ok(specialization_graph::NodeItem { - node: specialization_graph::Node::Impl(impl_def_id), + return Ok(specialization_graph::LeafDef { item: *item, + defining_node: impl_node, + finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) }, }); } } diff --git a/src/librustc_trait_selection/traits/specialize/mod.rs b/src/librustc_trait_selection/traits/specialize/mod.rs index edf02d75ee6..f98d8f2fabf 100644 --- a/src/librustc_trait_selection/traits/specialize/mod.rs +++ b/src/librustc_trait_selection/traits/specialize/mod.rs @@ -141,7 +141,7 @@ pub fn find_associated_item<'tcx>( param_env, impl_data.impl_def_id, substs, - node_item.node, + node_item.defining_node, ); infcx.tcx.erase_regions(&substs) }); diff --git a/src/librustc_trait_selection/traits/util.rs b/src/librustc_trait_selection/traits/util.rs index 6eeac2f676c..c28628678a9 100644 --- a/src/librustc_trait_selection/traits/util.rs +++ b/src/librustc_trait_selection/traits/util.rs @@ -4,11 +4,11 @@ use smallvec::smallvec; use smallvec::SmallVec; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::outlives::Component; use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_middle::traits::util::impl_is_default; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; @@ -651,20 +651,6 @@ pub fn generator_trait_ref_and_outputs( ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty)) } -pub fn impl_is_default(tcx: TyCtxt<'_>, node_item_def_id: DefId) -> bool { - match tcx.hir().as_local_hir_id(node_item_def_id) { - Some(hir_id) => { - let item = tcx.hir().expect_item(hir_id); - if let hir::ItemKind::Impl { defaultness, .. } = item.kind { - defaultness.is_default() - } else { - false - } - } - None => tcx.impl_defaultness(node_item_def_id).is_default(), - } -} - pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { assoc_item.defaultness.is_final() && !impl_is_default(tcx, assoc_item.container.id()) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 6249e1d4977..c85b5a4f2a2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -110,6 +110,7 @@ use rustc_infer::infer::{self, InferCtxt, InferOk, InferResult, TyCtxtInferExt}; use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::middle::region; use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::traits::util::impl_is_default; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, }; @@ -1942,7 +1943,7 @@ fn check_specialization_validity<'tcx>( // grandparent. In that case, if parent is a `default impl`, inherited items use the // "defaultness" from the grandparent, else they are final. None => { - if traits::impl_is_default(tcx, parent_impl.def_id()) { + if impl_is_default(tcx, parent_impl.def_id()) { None } else { Some(Err(parent_impl.def_id())) @@ -2114,10 +2115,10 @@ fn check_impl_items_against_trait<'tcx>( for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { let is_implemented = ancestors .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.node.is_from_trait()) + .map(|node_item| !node_item.defining_node.is_from_trait()) .unwrap_or(false); - if !is_implemented && !traits::impl_is_default(tcx, impl_id) { + if !is_implemented && !impl_is_default(tcx, impl_id) { if !trait_item.defaultness.has_value() { missing_items.push(*trait_item); }