pub mod select;
pub mod specialization_graph;
mod structural_impls;
+pub mod util;
use crate::mir::interpret::ErrorHandled;
use crate::ty::subst::SubstsRef;
}
}
-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<T> 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<Node>,
+}
+
+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> {
tcx: TyCtxt<'tcx>,
trait_item_name: Ident,
trait_item_kind: ty::AssocKind,
- ) -> Option<NodeItem> {
+ ) -> Option<LeafDef> {
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
+ }
})
}
}
--- /dev/null
+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(),
+ }
+}
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,
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={}",
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)
selcx: &SelectionContext<'_, '_>,
impl_def_id: DefId,
assoc_ty_def_id: DefId,
-) -> Result<specialization_graph::NodeItem, ErrorReported> {
+) -> Result<specialization_graph::LeafDef, ErrorReported> {
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;
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) },
});
}
}
param_env,
impl_data.impl_def_id,
substs,
- node_item.node,
+ node_item.defining_node,
);
infcx.tcx.erase_regions(&substs)
});
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};
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())
}
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,
};
// 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()))
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);
}