mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
spec. graph: track defining and finalizing impls
This commit is contained in:
parent
a1e7495a41
commit
49ba323c8d
@ -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;
|
||||
|
@ -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<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> {
|
||||
@ -167,11 +203,27 @@ 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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
17
src/librustc_middle/traits/util.rs
Normal file
17
src/librustc_middle/traits/util.rs
Normal file
@ -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(),
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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<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;
|
||||
@ -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) },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
});
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user