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 select;
|
||||||
pub mod specialization_graph;
|
pub mod specialization_graph;
|
||||||
mod structural_impls;
|
mod structural_impls;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
use crate::mir::interpret::ErrorHandled;
|
use crate::mir::interpret::ErrorHandled;
|
||||||
use crate::ty::subst::SubstsRef;
|
use crate::ty::subst::SubstsRef;
|
||||||
|
@ -154,9 +154,45 @@ impl Iterator for Ancestors<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NodeItem {
|
/// Information about the most specialized definition of an associated item.
|
||||||
pub node: Node,
|
pub struct LeafDef {
|
||||||
|
/// The associated item described by this `LeafDef`.
|
||||||
pub item: ty::AssocItem,
|
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> {
|
impl<'tcx> Ancestors<'tcx> {
|
||||||
@ -167,11 +203,27 @@ impl<'tcx> Ancestors<'tcx> {
|
|||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_item_name: Ident,
|
trait_item_name: Ident,
|
||||||
trait_item_kind: ty::AssocKind,
|
trait_item_kind: ty::AssocKind,
|
||||||
) -> Option<NodeItem> {
|
) -> Option<LeafDef> {
|
||||||
let trait_def_id = self.trait_def_id;
|
let trait_def_id = self.trait_def_id;
|
||||||
|
let mut finalizing_node = None;
|
||||||
|
|
||||||
self.find_map(|node| {
|
self.find_map(|node| {
|
||||||
node.item(tcx, trait_item_name, trait_item_kind, trait_def_id)
|
if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) {
|
||||||
.map(|item| NodeItem { node, item })
|
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::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs};
|
||||||
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
|
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
|
||||||
pub use self::util::{
|
pub use self::util::{
|
||||||
get_vtable_index_of_object_method, impl_is_default, impl_item_is_final,
|
get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices,
|
||||||
predicate_for_trait_def, upcast_choices,
|
|
||||||
};
|
};
|
||||||
pub use self::util::{
|
pub use self::util::{
|
||||||
supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits,
|
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)
|
assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id)
|
||||||
.map_err(|ErrorReported| ())?;
|
.map_err(|ErrorReported| ())?;
|
||||||
|
|
||||||
let is_default = if node_item.node.is_from_trait() {
|
if node_item.is_final() {
|
||||||
// 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 {
|
|
||||||
// Non-specializable items are always projectable
|
// Non-specializable items are always projectable
|
||||||
false => true,
|
true
|
||||||
|
} else {
|
||||||
// Only reveal a specializable default if we're past type-checking
|
// Only reveal a specializable default if we're past type-checking
|
||||||
// and the obligation is monomorphic, otherwise passes such as
|
// and the obligation is monomorphic, otherwise passes such as
|
||||||
// transmute checking and polymorphic MIR optimizations could
|
// transmute checking and polymorphic MIR optimizations could
|
||||||
// get a result which isn't correct for all monomorphizations.
|
// 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
|
// NOTE(eddyb) inference variables can resolve to parameters, so
|
||||||
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
|
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
|
||||||
let poly_trait_ref =
|
let poly_trait_ref =
|
||||||
selcx.infcx().resolve_vars_if_possible(&poly_trait_ref);
|
selcx.infcx().resolve_vars_if_possible(&poly_trait_ref);
|
||||||
!poly_trait_ref.needs_infer() && !poly_trait_ref.needs_subst()
|
!poly_trait_ref.needs_infer() && !poly_trait_ref.needs_subst()
|
||||||
}
|
} else {
|
||||||
|
|
||||||
true => {
|
|
||||||
debug!(
|
debug!(
|
||||||
"assemble_candidates_from_impls: not eligible due to default: \
|
"assemble_candidates_from_impls: not eligible due to default: \
|
||||||
assoc_ty={} predicate={}",
|
assoc_ty={} predicate={}",
|
||||||
@ -1422,7 +1394,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
|
|||||||
return Progress { ty: tcx.types.err, obligations: nested };
|
return Progress { ty: tcx.types.err, obligations: nested };
|
||||||
}
|
}
|
||||||
let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs);
|
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 ty = if let ty::AssocKind::OpaqueTy = assoc_ty.item.kind {
|
||||||
let item_substs = InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id);
|
let item_substs = InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id);
|
||||||
tcx.mk_opaque(assoc_ty.item.def_id, item_substs)
|
tcx.mk_opaque(assoc_ty.item.def_id, item_substs)
|
||||||
@ -1447,7 +1420,7 @@ fn assoc_ty_def(
|
|||||||
selcx: &SelectionContext<'_, '_>,
|
selcx: &SelectionContext<'_, '_>,
|
||||||
impl_def_id: DefId,
|
impl_def_id: DefId,
|
||||||
assoc_ty_def_id: DefId,
|
assoc_ty_def_id: DefId,
|
||||||
) -> Result<specialization_graph::NodeItem, ErrorReported> {
|
) -> Result<specialization_graph::LeafDef, ErrorReported> {
|
||||||
let tcx = selcx.tcx();
|
let tcx = selcx.tcx();
|
||||||
let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident;
|
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;
|
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)
|
if matches!(item.kind, ty::AssocKind::Type | ty::AssocKind::OpaqueTy)
|
||||||
&& tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id)
|
&& tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id)
|
||||||
{
|
{
|
||||||
return Ok(specialization_graph::NodeItem {
|
return Ok(specialization_graph::LeafDef {
|
||||||
node: specialization_graph::Node::Impl(impl_def_id),
|
|
||||||
item: *item,
|
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,
|
param_env,
|
||||||
impl_data.impl_def_id,
|
impl_data.impl_def_id,
|
||||||
substs,
|
substs,
|
||||||
node_item.node,
|
node_item.defining_node,
|
||||||
);
|
);
|
||||||
infcx.tcx.erase_regions(&substs)
|
infcx.tcx.erase_regions(&substs)
|
||||||
});
|
});
|
||||||
|
@ -4,11 +4,11 @@ use smallvec::smallvec;
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_hir as hir;
|
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::ty::outlives::Component;
|
use rustc_middle::ty::outlives::Component;
|
||||||
use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
|
use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
|
||||||
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness};
|
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};
|
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))
|
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 {
|
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())
|
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::hir::map::blocks::FnLikeNode;
|
||||||
use rustc_middle::middle::region;
|
use rustc_middle::middle::region;
|
||||||
use rustc_middle::mir::interpret::ConstValue;
|
use rustc_middle::mir::interpret::ConstValue;
|
||||||
|
use rustc_middle::traits::util::impl_is_default;
|
||||||
use rustc_middle::ty::adjustment::{
|
use rustc_middle::ty::adjustment::{
|
||||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
|
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
|
// grandparent. In that case, if parent is a `default impl`, inherited items use the
|
||||||
// "defaultness" from the grandparent, else they are final.
|
// "defaultness" from the grandparent, else they are final.
|
||||||
None => {
|
None => {
|
||||||
if traits::impl_is_default(tcx, parent_impl.def_id()) {
|
if impl_is_default(tcx, parent_impl.def_id()) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Err(parent_impl.def_id()))
|
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() {
|
for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() {
|
||||||
let is_implemented = ancestors
|
let is_implemented = ancestors
|
||||||
.leaf_def(tcx, trait_item.ident, trait_item.kind)
|
.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);
|
.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() {
|
if !trait_item.defaultness.has_value() {
|
||||||
missing_items.push(*trait_item);
|
missing_items.push(*trait_item);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user