Skip to content

Commit 0e0d84c

Browse files
authored
Rollup merge of #70535 - jonas-schievink:graph-refactor, r=nikomatsakis
Track the finalizing node in the specialization graph Fixes #70419 Fixes #70442 r? @eddyb
2 parents 235938d + fd8f818 commit 0e0d84c

File tree

9 files changed

+149
-121
lines changed

9 files changed

+149
-121
lines changed

src/librustc_middle/traits/specialization_graph.rs

+59-11
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,44 @@ impl Iterator for Ancestors<'_> {
154154
}
155155
}
156156

157-
pub struct NodeItem<T> {
158-
pub node: Node,
159-
pub item: T,
157+
/// Information about the most specialized definition of an associated item.
158+
pub struct LeafDef {
159+
/// The associated item described by this `LeafDef`.
160+
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>,
160189
}
161190

162-
impl<T> NodeItem<T> {
163-
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> NodeItem<U> {
164-
NodeItem { node: self.node, item: f(self.item) }
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()
165195
}
166196
}
167197

@@ -173,18 +203,36 @@ impl<'tcx> Ancestors<'tcx> {
173203
tcx: TyCtxt<'tcx>,
174204
trait_item_name: Ident,
175205
trait_item_kind: ty::AssocKind,
176-
) -> Option<NodeItem<ty::AssocItem>> {
206+
) -> Option<LeafDef> {
177207
let trait_def_id = self.trait_def_id;
208+
let mut finalizing_node = None;
209+
178210
self.find_map(|node| {
179-
node.item(tcx, trait_item_name, trait_item_kind, trait_def_id)
180-
.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+
|| tcx.impl_defaultness(node.def_id()).is_default();
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+
}
181227
})
182228
}
183229
}
184230

185231
/// Walk up the specialization ancestors of a given impl, starting with that
186-
/// impl itself. Returns `None` if an error was reported while building the
187-
/// specialization graph.
232+
/// impl itself.
233+
///
234+
/// Returns `Err` if an error was reported while building the specialization
235+
/// graph.
188236
pub fn ancestors(
189237
tcx: TyCtxt<'tcx>,
190238
trait_def_id: DefId,

src/librustc_trait_selection/traits/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ pub use self::project::{
5454
};
5555
pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
5656
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
57-
pub use self::specialize::find_associated_item;
5857
pub use self::specialize::specialization_graph::FutureCompatOverlapError;
5958
pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
6059
pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
@@ -64,8 +63,7 @@ pub use self::structural_match::NonStructuralMatchTy;
6463
pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs};
6564
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
6665
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,
66+
get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices,
6967
};
7068
pub use self::util::{
7169
supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits,

src/librustc_trait_selection/traits/project.rs

+11-37
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
1018+
if node_item.is_final() {
1019+
// Non-specializable items are always projectable.
1020+
true
10361021
} 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 {
1045-
// Non-specializable items are always projectable
1046-
false => true,
1047-
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<ty::AssocItem>, 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

+1-43
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_errors::struct_span_err;
2020
use rustc_hir::def_id::DefId;
2121
use rustc_middle::lint::LintDiagnosticBuilder;
2222
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
23-
use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
23+
use rustc_middle::ty::{self, TyCtxt};
2424
use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
2525
use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
2626
use rustc_span::DUMMY_SP;
@@ -112,48 +112,6 @@ pub fn translate_substs<'a, 'tcx>(
112112
source_substs.rebase_onto(infcx.tcx, source_impl, target_substs)
113113
}
114114

115-
/// Given a selected impl described by `impl_data`, returns the
116-
/// definition and substitutions for the method with the name `name`
117-
/// the kind `kind`, and trait method substitutions `substs`, in
118-
/// that impl, a less specialized impl, or the trait default,
119-
/// whichever applies.
120-
pub fn find_associated_item<'tcx>(
121-
tcx: TyCtxt<'tcx>,
122-
param_env: ty::ParamEnv<'tcx>,
123-
item: &ty::AssocItem,
124-
substs: SubstsRef<'tcx>,
125-
impl_data: &super::VtableImplData<'tcx, ()>,
126-
) -> (DefId, SubstsRef<'tcx>) {
127-
debug!("find_associated_item({:?}, {:?}, {:?}, {:?})", param_env, item, substs, impl_data);
128-
assert!(!substs.needs_infer());
129-
130-
let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap();
131-
let trait_def = tcx.trait_def(trait_def_id);
132-
133-
if let Ok(ancestors) = trait_def.ancestors(tcx, impl_data.impl_def_id) {
134-
match ancestors.leaf_def(tcx, item.ident, item.kind) {
135-
Some(node_item) => {
136-
let substs = tcx.infer_ctxt().enter(|infcx| {
137-
let param_env = param_env.with_reveal_all();
138-
let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
139-
let substs = translate_substs(
140-
&infcx,
141-
param_env,
142-
impl_data.impl_def_id,
143-
substs,
144-
node_item.node,
145-
);
146-
infcx.tcx.erase_regions(&substs)
147-
});
148-
(node_item.item.def_id, substs)
149-
}
150-
None => bug!("{:?} not found in {:?}", item, impl_data.impl_def_id),
151-
}
152-
} else {
153-
(item.def_id, substs)
154-
}
155-
}
156-
157115
/// Is `impl1` a specialization of `impl2`?
158116
///
159117
/// Specialization is determined by the sets of types to which the impls apply;

src/librustc_trait_selection/traits/util.rs

+1-16
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ 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};
@@ -651,22 +650,8 @@ pub fn generator_trait_ref_and_outputs(
651650
ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
652651
}
653652

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-
668653
pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
669-
assoc_item.defaultness.is_final() && !impl_is_default(tcx, assoc_item.container.id())
654+
assoc_item.defaultness.is_final() && tcx.impl_defaultness(assoc_item.container.id()).is_final()
670655
}
671656

672657
pub enum TupleArgumentsFlag {

src/librustc_ty/instance.rs

+39-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use rustc_hir::def_id::DefId;
2+
use rustc_infer::infer::TyCtxtInferExt;
23
use rustc_middle::ty::subst::SubstsRef;
34
use rustc_middle::ty::{self, Instance, TyCtxt, TypeFoldable};
45
use rustc_span::sym;
56
use rustc_target::spec::abi::Abi;
67
use rustc_trait_selection::traits;
8+
use traits::{translate_substs, Reveal};
79

810
use log::debug;
911

@@ -82,21 +84,50 @@ fn resolve_associated_item<'tcx>(
8284
// the actual function:
8385
match vtbl {
8486
traits::VtableImpl(impl_data) => {
85-
let (def_id, substs) =
86-
traits::find_associated_item(tcx, param_env, trait_item, rcvr_substs, &impl_data);
87-
88-
let resolved_item = tcx.associated_item(def_id);
87+
debug!(
88+
"resolving VtableImpl: {:?}, {:?}, {:?}, {:?}",
89+
param_env, trait_item, rcvr_substs, impl_data
90+
);
91+
assert!(!rcvr_substs.needs_infer());
92+
assert!(!trait_ref.needs_infer());
93+
94+
let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap();
95+
let trait_def = tcx.trait_def(trait_def_id);
96+
let leaf_def = trait_def
97+
.ancestors(tcx, impl_data.impl_def_id)
98+
.ok()?
99+
.leaf_def(tcx, trait_item.ident, trait_item.kind)
100+
.unwrap_or_else(|| {
101+
bug!("{:?} not found in {:?}", trait_item, impl_data.impl_def_id);
102+
});
103+
let def_id = leaf_def.item.def_id;
104+
105+
let substs = tcx.infer_ctxt().enter(|infcx| {
106+
let param_env = param_env.with_reveal_all();
107+
let substs = rcvr_substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
108+
let substs = translate_substs(
109+
&infcx,
110+
param_env,
111+
impl_data.impl_def_id,
112+
substs,
113+
leaf_def.defining_node,
114+
);
115+
infcx.tcx.erase_regions(&substs)
116+
});
89117

90118
// Since this is a trait item, we need to see if the item is either a trait default item
91119
// or a specialization because we can't resolve those unless we can `Reveal::All`.
92120
// NOTE: This should be kept in sync with the similar code in
93121
// `rustc_middle::traits::project::assemble_candidates_from_impls()`.
94-
let eligible = if !resolved_item.defaultness.is_default() {
122+
let eligible = if leaf_def.is_final() {
123+
// Non-specializable items are always projectable.
95124
true
96-
} else if param_env.reveal == traits::Reveal::All {
97-
!trait_ref.needs_subst()
98125
} else {
99-
false
126+
// Only reveal a specializable default if we're past type-checking
127+
// and the obligation is monomorphic, otherwise passes such as
128+
// transmute checking and polymorphic MIR optimizations could
129+
// get a result which isn't correct for all monomorphizations.
130+
if param_env.reveal == Reveal::All { !trait_ref.needs_subst() } else { false }
100131
};
101132

102133
if !eligible {

src/librustc_ty/ty.rs

+11
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem {
165165
)
166166
}
167167

168+
fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness {
169+
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
170+
let item = tcx.hir().expect_item(hir_id);
171+
if let hir::ItemKind::Impl { defaultness, .. } = item.kind {
172+
defaultness
173+
} else {
174+
bug!("`impl_defaultness` called on {:?}", item);
175+
}
176+
}
177+
168178
/// Calculates the `Sized` constraint.
169179
///
170180
/// In fact, there are only a few options for the types in the constraint:
@@ -371,6 +381,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
371381
crate_hash,
372382
instance_def_size_estimate,
373383
issue33140_self_ty,
384+
impl_defaultness,
374385
..*providers
375386
};
376387
}

0 commit comments

Comments
 (0)