Skip to content

Commit 49ba323

Browse files
spec. graph: track defining and finalizing impls
1 parent a1e7495 commit 49ba323

File tree

8 files changed

+93
-63
lines changed

8 files changed

+93
-63
lines changed

src/librustc_middle/traits/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod query;
66
pub mod select;
77
pub mod specialization_graph;
88
mod structural_impls;
9+
pub mod util;
910

1011
use crate::mir::interpret::ErrorHandled;
1112
use crate::ty::subst::SubstsRef;

src/librustc_middle/traits/specialization_graph.rs

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,45 @@ impl Iterator for Ancestors<'_> {
154154
}
155155
}
156156

157-
pub struct NodeItem {
158-
pub node: Node,
157+
/// Information about the most specialized definition of an associated item.
158+
pub struct LeafDef {
159+
/// The associated item described by this `LeafDef`.
159160
pub item: ty::AssocItem,
161+
162+
/// The node in the specialization graph containing the definition of `item`.
163+
pub defining_node: Node,
164+
165+
/// The "top-most" (ie. least specialized) specialization graph node that finalized the
166+
/// definition of `item`.
167+
///
168+
/// Example:
169+
///
170+
/// ```
171+
/// trait Tr {
172+
/// fn assoc(&self);
173+
/// }
174+
///
175+
/// impl<T> Tr for T {
176+
/// default fn assoc(&self) {}
177+
/// }
178+
///
179+
/// impl Tr for u8 {}
180+
/// ```
181+
///
182+
/// If we start the leaf definition search at `impl Tr for u8`, that impl will be the
183+
/// `finalizing_node`, while `defining_node` will be the generic impl.
184+
///
185+
/// If the leaf definition search is started at the generic impl, `finalizing_node` will be
186+
/// `None`, since the most specialized impl we found still allows overriding the method
187+
/// (doesn't finalize it).
188+
pub finalizing_node: Option<Node>,
189+
}
190+
191+
impl LeafDef {
192+
/// Returns whether this definition is known to not be further specializable.
193+
pub fn is_final(&self) -> bool {
194+
self.finalizing_node.is_some()
195+
}
160196
}
161197

162198
impl<'tcx> Ancestors<'tcx> {
@@ -167,11 +203,27 @@ impl<'tcx> Ancestors<'tcx> {
167203
tcx: TyCtxt<'tcx>,
168204
trait_item_name: Ident,
169205
trait_item_kind: ty::AssocKind,
170-
) -> Option<NodeItem> {
206+
) -> Option<LeafDef> {
171207
let trait_def_id = self.trait_def_id;
208+
let mut finalizing_node = None;
209+
172210
self.find_map(|node| {
173-
node.item(tcx, trait_item_name, trait_item_kind, trait_def_id)
174-
.map(|item| NodeItem { node, item })
211+
if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) {
212+
if finalizing_node.is_none() {
213+
let is_specializable = item.defaultness.is_default()
214+
|| super::util::impl_is_default(tcx, node.def_id());
215+
216+
if !is_specializable {
217+
finalizing_node = Some(node);
218+
}
219+
}
220+
221+
Some(LeafDef { item, defining_node: node, finalizing_node })
222+
} else {
223+
// Item not mentioned. This "finalizes" any defaulted item provided by an ancestor.
224+
finalizing_node = Some(node);
225+
None
226+
}
175227
})
176228
}
177229
}

src/librustc_middle/traits/util.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use crate::ty::TyCtxt;
2+
use rustc_hir as hir;
3+
use rustc_hir::def_id::DefId;
4+
5+
pub fn impl_is_default(tcx: TyCtxt<'_>, node_item_def_id: DefId) -> bool {
6+
match tcx.hir().as_local_hir_id(node_item_def_id) {
7+
Some(hir_id) => {
8+
let item = tcx.hir().expect_item(hir_id);
9+
if let hir::ItemKind::Impl { defaultness, .. } = item.kind {
10+
defaultness.is_default()
11+
} else {
12+
false
13+
}
14+
}
15+
None => tcx.impl_defaultness(node_item_def_id).is_default(),
16+
}
17+
}

src/librustc_trait_selection/traits/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ pub use self::structural_match::NonStructuralMatchTy;
6464
pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs};
6565
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
6666
pub use self::util::{
67-
get_vtable_index_of_object_method, impl_is_default, impl_item_is_final,
68-
predicate_for_trait_def, upcast_choices,
67+
get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices,
6968
};
7069
pub use self::util::{
7170
supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits,

src/librustc_trait_selection/traits/project.rs

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,49 +1015,21 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
10151015
assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id)
10161016
.map_err(|ErrorReported| ())?;
10171017

1018-
let is_default = if node_item.node.is_from_trait() {
1019-
// If true, the impl inherited a `type Foo = Bar`
1020-
// given in the trait, which is implicitly default.
1021-
// Otherwise, the impl did not specify `type` and
1022-
// neither did the trait:
1023-
//
1024-
// ```rust
1025-
// trait Foo { type T; }
1026-
// impl Foo for Bar { }
1027-
// ```
1028-
//
1029-
// This is an error, but it will be
1030-
// reported in `check_impl_items_against_trait`.
1031-
// We accept it here but will flag it as
1032-
// an error when we confirm the candidate
1033-
// (which will ultimately lead to `normalize_to_error`
1034-
// being invoked).
1035-
false
1036-
} else {
1037-
// If we're looking at a trait *impl*, the item is
1038-
// specializable if the impl or the item are marked
1039-
// `default`.
1040-
node_item.item.defaultness.is_default()
1041-
|| super::util::impl_is_default(selcx.tcx(), node_item.node.def_id())
1042-
};
1043-
1044-
match is_default {
1018+
if node_item.is_final() {
10451019
// Non-specializable items are always projectable
1046-
false => true,
1047-
1020+
true
1021+
} else {
10481022
// Only reveal a specializable default if we're past type-checking
10491023
// and the obligation is monomorphic, otherwise passes such as
10501024
// transmute checking and polymorphic MIR optimizations could
10511025
// get a result which isn't correct for all monomorphizations.
1052-
true if obligation.param_env.reveal == Reveal::All => {
1026+
if obligation.param_env.reveal == Reveal::All {
10531027
// NOTE(eddyb) inference variables can resolve to parameters, so
10541028
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
10551029
let poly_trait_ref =
10561030
selcx.infcx().resolve_vars_if_possible(&poly_trait_ref);
10571031
!poly_trait_ref.needs_infer() && !poly_trait_ref.needs_subst()
1058-
}
1059-
1060-
true => {
1032+
} else {
10611033
debug!(
10621034
"assemble_candidates_from_impls: not eligible due to default: \
10631035
assoc_ty={} predicate={}",
@@ -1422,7 +1394,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
14221394
return Progress { ty: tcx.types.err, obligations: nested };
14231395
}
14241396
let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs);
1425-
let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.node);
1397+
let substs =
1398+
translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node);
14261399
let ty = if let ty::AssocKind::OpaqueTy = assoc_ty.item.kind {
14271400
let item_substs = InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id);
14281401
tcx.mk_opaque(assoc_ty.item.def_id, item_substs)
@@ -1447,7 +1420,7 @@ fn assoc_ty_def(
14471420
selcx: &SelectionContext<'_, '_>,
14481421
impl_def_id: DefId,
14491422
assoc_ty_def_id: DefId,
1450-
) -> Result<specialization_graph::NodeItem, ErrorReported> {
1423+
) -> Result<specialization_graph::LeafDef, ErrorReported> {
14511424
let tcx = selcx.tcx();
14521425
let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident;
14531426
let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
@@ -1464,9 +1437,10 @@ fn assoc_ty_def(
14641437
if matches!(item.kind, ty::AssocKind::Type | ty::AssocKind::OpaqueTy)
14651438
&& tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id)
14661439
{
1467-
return Ok(specialization_graph::NodeItem {
1468-
node: specialization_graph::Node::Impl(impl_def_id),
1440+
return Ok(specialization_graph::LeafDef {
14691441
item: *item,
1442+
defining_node: impl_node,
1443+
finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) },
14701444
});
14711445
}
14721446
}

src/librustc_trait_selection/traits/specialize/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ pub fn find_associated_item<'tcx>(
141141
param_env,
142142
impl_data.impl_def_id,
143143
substs,
144-
node_item.node,
144+
node_item.defining_node,
145145
);
146146
infcx.tcx.erase_regions(&substs)
147147
});

src/librustc_trait_selection/traits/util.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ use smallvec::smallvec;
44
use smallvec::SmallVec;
55

66
use rustc_data_structures::fx::FxHashSet;
7-
use rustc_hir as hir;
87
use rustc_hir::def_id::DefId;
98
use rustc_middle::ty::outlives::Component;
109
use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
1110
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness};
11+
use rustc_middle::traits::util::impl_is_default;
1212

1313
use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
1414

@@ -651,20 +651,6 @@ pub fn generator_trait_ref_and_outputs(
651651
ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
652652
}
653653

654-
pub fn impl_is_default(tcx: TyCtxt<'_>, node_item_def_id: DefId) -> bool {
655-
match tcx.hir().as_local_hir_id(node_item_def_id) {
656-
Some(hir_id) => {
657-
let item = tcx.hir().expect_item(hir_id);
658-
if let hir::ItemKind::Impl { defaultness, .. } = item.kind {
659-
defaultness.is_default()
660-
} else {
661-
false
662-
}
663-
}
664-
None => tcx.impl_defaultness(node_item_def_id).is_default(),
665-
}
666-
}
667-
668654
pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
669655
assoc_item.defaultness.is_final() && !impl_is_default(tcx, assoc_item.container.id())
670656
}

src/librustc_typeck/check/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ use rustc_infer::infer::{self, InferCtxt, InferOk, InferResult, TyCtxtInferExt};
110110
use rustc_middle::hir::map::blocks::FnLikeNode;
111111
use rustc_middle::middle::region;
112112
use rustc_middle::mir::interpret::ConstValue;
113+
use rustc_middle::traits::util::impl_is_default;
113114
use rustc_middle::ty::adjustment::{
114115
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
115116
};
@@ -1942,7 +1943,7 @@ fn check_specialization_validity<'tcx>(
19421943
// grandparent. In that case, if parent is a `default impl`, inherited items use the
19431944
// "defaultness" from the grandparent, else they are final.
19441945
None => {
1945-
if traits::impl_is_default(tcx, parent_impl.def_id()) {
1946+
if impl_is_default(tcx, parent_impl.def_id()) {
19461947
None
19471948
} else {
19481949
Some(Err(parent_impl.def_id()))
@@ -2114,10 +2115,10 @@ fn check_impl_items_against_trait<'tcx>(
21142115
for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() {
21152116
let is_implemented = ancestors
21162117
.leaf_def(tcx, trait_item.ident, trait_item.kind)
2117-
.map(|node_item| !node_item.node.is_from_trait())
2118+
.map(|node_item| !node_item.defining_node.is_from_trait())
21182119
.unwrap_or(false);
21192120

2120-
if !is_implemented && !traits::impl_is_default(tcx, impl_id) {
2121+
if !is_implemented && !impl_is_default(tcx, impl_id) {
21212122
if !trait_item.defaultness.has_value() {
21222123
missing_items.push(*trait_item);
21232124
}

0 commit comments

Comments
 (0)