Skip to content

Commit 44e526b

Browse files
authored
Rollup merge of #80732 - spastorino:trait-inheritance-self2, r=nikomatsakis
Allow Trait inheritance with cycles on associated types take 2 This reverts the revert of #79209 and fixes the ICEs that's occasioned by that PR exposing some problems that are addressed in #80648 and #79811. For easier review I'd say, check only the last commit, the first one is just a revert of the revert of #79209 which was already approved. This also could be considered part or the actual fix of #79560 but I guess for that to be closed and fixed completely we would need to land #80648 and #79811 too. r? `@nikomatsakis` cc `@Aaron1011`
2 parents f8b330d + 8d17c6a commit 44e526b

30 files changed

+582
-151
lines changed

compiler/rustc_infer/src/traits/util.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use smallvec::smallvec;
22

33
use crate::traits::{Obligation, ObligationCause, PredicateObligation};
4-
use rustc_data_structures::fx::FxHashSet;
4+
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
55
use rustc_middle::ty::outlives::Component;
66
use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness};
7+
use rustc_span::symbol::Ident;
78

89
pub fn anonymize_predicate<'tcx>(
910
tcx: TyCtxt<'tcx>,
@@ -282,6 +283,44 @@ pub fn transitive_bounds<'tcx>(
282283
elaborate_trait_refs(tcx, bounds).filter_to_traits()
283284
}
284285

286+
/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may
287+
/// define the given associated type `assoc_name`. It uses the
288+
/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that
289+
/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
290+
/// `T::Item` and helps to avoid cycle errors (see e.g. #35237).
291+
pub fn transitive_bounds_that_define_assoc_type<'tcx>(
292+
tcx: TyCtxt<'tcx>,
293+
bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
294+
assoc_name: Ident,
295+
) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> {
296+
let mut stack: Vec<_> = bounds.collect();
297+
let mut visited = FxIndexSet::default();
298+
299+
std::iter::from_fn(move || {
300+
while let Some(trait_ref) = stack.pop() {
301+
let anon_trait_ref = tcx.anonymize_late_bound_regions(trait_ref);
302+
if visited.insert(anon_trait_ref) {
303+
let super_predicates = tcx.super_predicates_that_define_assoc_type((
304+
trait_ref.def_id(),
305+
Some(assoc_name),
306+
));
307+
for (super_predicate, _) in super_predicates.predicates {
308+
let bound_predicate = super_predicate.kind();
309+
let subst_predicate = super_predicate
310+
.subst_supertrait(tcx, &bound_predicate.rebind(trait_ref.skip_binder()));
311+
if let Some(binder) = subst_predicate.to_opt_poly_trait_ref() {
312+
stack.push(binder.value);
313+
}
314+
}
315+
316+
return Some(trait_ref);
317+
}
318+
}
319+
320+
return None;
321+
})
322+
}
323+
285324
///////////////////////////////////////////////////////////////////////////
286325
// Other
287326
///////////////////////////////////////////////////////////////////////////

compiler/rustc_middle/src/query/mod.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -443,12 +443,23 @@ rustc_queries! {
443443
/// full predicates are available (note that supertraits have
444444
/// additional acyclicity requirements).
445445
query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
446-
desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) }
446+
desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) }
447+
}
448+
449+
/// The `Option<Ident>` is the name of an associated type. If it is `None`, then this query
450+
/// returns the full set of predicates. If `Some<Ident>`, then the query returns only the
451+
/// subset of super-predicates that reference traits that define the given associated type.
452+
/// This is used to avoid cycles in resolving types like `T::Item`.
453+
query super_predicates_that_define_assoc_type(key: (DefId, Option<rustc_span::symbol::Ident>)) -> ty::GenericPredicates<'tcx> {
454+
desc { |tcx| "computing the super traits of `{}`{}",
455+
tcx.def_path_str(key.0),
456+
if let Some(assoc_name) = key.1 { format!(" with associated type name `{}`", assoc_name) } else { "".to_string() },
457+
}
447458
}
448459

449460
/// To avoid cycles within the predicates of a single item we compute
450461
/// per-type-parameter predicates for resolving `T::AssocTy`.
451-
query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> {
462+
query type_param_predicates(key: (DefId, LocalDefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> {
452463
desc { |tcx| "computing the bounds for type parameter `{}`", {
453464
let id = tcx.hir().local_def_id_to_hir_id(key.1);
454465
tcx.hir().ty_param_name(id)

compiler/rustc_middle/src/ty/context.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames};
5252
use rustc_session::lint::{Level, Lint};
5353
use rustc_session::Session;
5454
use rustc_span::source_map::MultiSpan;
55-
use rustc_span::symbol::{kw, sym, Symbol};
55+
use rustc_span::symbol::{kw, sym, Ident, Symbol};
5656
use rustc_span::{Span, DUMMY_SP};
5757
use rustc_target::abi::{Layout, TargetDataLayout, VariantIdx};
5858
use rustc_target::spec::abi;
@@ -2053,6 +2053,42 @@ impl<'tcx> TyCtxt<'tcx> {
20532053
self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }))
20542054
}
20552055

2056+
/// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name`
2057+
/// returns true if the `trait_def_id` defines an associated item of name `assoc_name`.
2058+
pub fn trait_may_define_assoc_type(self, trait_def_id: DefId, assoc_name: Ident) -> bool {
2059+
self.super_traits_of(trait_def_id).any(|trait_did| {
2060+
self.associated_items(trait_did)
2061+
.find_by_name_and_kind(self, assoc_name, ty::AssocKind::Type, trait_did)
2062+
.is_some()
2063+
})
2064+
}
2065+
2066+
/// Computes the def-ids of the transitive super-traits of `trait_def_id`. This (intentionally)
2067+
/// does not compute the full elaborated super-predicates but just the set of def-ids. It is used
2068+
/// to identify which traits may define a given associated type to help avoid cycle errors.
2069+
/// Returns a `DefId` iterator.
2070+
fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
2071+
let mut set = FxHashSet::default();
2072+
let mut stack = vec![trait_def_id];
2073+
2074+
set.insert(trait_def_id);
2075+
2076+
iter::from_fn(move || -> Option<DefId> {
2077+
let trait_did = stack.pop()?;
2078+
let generic_predicates = self.super_predicates_of(trait_did);
2079+
2080+
for (predicate, _) in generic_predicates.predicates {
2081+
if let ty::PredicateKind::Trait(data, _) = predicate.kind().skip_binder() {
2082+
if set.insert(data.def_id()) {
2083+
stack.push(data.def_id());
2084+
}
2085+
}
2086+
}
2087+
2088+
Some(trait_did)
2089+
})
2090+
}
2091+
20562092
/// Given a closure signature, returns an equivalent fn signature. Detuples
20572093
/// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then
20582094
/// you would get a `fn(u32, i32)`.

compiler/rustc_middle/src/ty/query/keys.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::ty::subst::{GenericArg, SubstsRef};
77
use crate::ty::{self, Ty, TyCtxt};
88
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
99
use rustc_query_system::query::DefaultCacheSelector;
10-
use rustc_span::symbol::Symbol;
10+
use rustc_span::symbol::{Ident, Symbol};
1111
use rustc_span::{Span, DUMMY_SP};
1212

1313
/// The `Key` trait controls what types can legally be used as the key
@@ -160,6 +160,28 @@ impl Key for (LocalDefId, DefId) {
160160
}
161161
}
162162

163+
impl Key for (DefId, Option<Ident>) {
164+
type CacheSelector = DefaultCacheSelector;
165+
166+
fn query_crate(&self) -> CrateNum {
167+
self.0.krate
168+
}
169+
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
170+
tcx.def_span(self.0)
171+
}
172+
}
173+
174+
impl Key for (DefId, LocalDefId, Ident) {
175+
type CacheSelector = DefaultCacheSelector;
176+
177+
fn query_crate(&self) -> CrateNum {
178+
self.0.krate
179+
}
180+
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
181+
self.1.default_span(tcx)
182+
}
183+
}
184+
163185
impl Key for (CrateNum, DefId) {
164186
type CacheSelector = DefaultCacheSelector;
165187

compiler/rustc_trait_selection/src/traits/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ pub use self::util::{
6565
get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices,
6666
};
6767
pub use self::util::{
68-
supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits,
68+
supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_type,
69+
SupertraitDefIds, Supertraits,
6970
};
7071

7172
pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext;

compiler/rustc_typeck/src/astconv/mod.rs

+57-11
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,10 @@ pub trait AstConv<'tcx> {
4949

5050
fn default_constness_for_trait_bounds(&self) -> Constness;
5151

52-
/// Returns predicates in scope of the form `X: Foo`, where `X` is
53-
/// a type parameter `X` with the given id `def_id`. This is a
54-
/// subset of the full set of predicates.
52+
/// Returns predicates in scope of the form `X: Foo<T>`, where `X`
53+
/// is a type parameter `X` with the given id `def_id` and T
54+
/// matches `assoc_name`. This is a subset of the full set of
55+
/// predicates.
5556
///
5657
/// This is used for one specific purpose: resolving "short-hand"
5758
/// associated type references like `T::Item`. In principle, we
@@ -60,7 +61,12 @@ pub trait AstConv<'tcx> {
6061
/// but this can lead to cycle errors. The problem is that we have
6162
/// to do this resolution *in order to create the predicates in
6263
/// the first place*. Hence, we have this "special pass".
63-
fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx>;
64+
fn get_type_parameter_bounds(
65+
&self,
66+
span: Span,
67+
def_id: DefId,
68+
assoc_name: Ident,
69+
) -> ty::GenericPredicates<'tcx>;
6470

6571
/// Returns the lifetime to use when a lifetime is omitted (and not elided).
6672
fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span)
@@ -792,7 +798,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
792798
}
793799

794800
// Returns `true` if a bounds list includes `?Sized`.
795-
pub fn is_unsized(&self, ast_bounds: &[hir::GenericBound<'_>], span: Span) -> bool {
801+
pub fn is_unsized(&self, ast_bounds: &[&hir::GenericBound<'_>], span: Span) -> bool {
796802
let tcx = self.tcx();
797803

798804
// Try to find an unbound in bounds.
@@ -850,7 +856,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
850856
fn add_bounds(
851857
&self,
852858
param_ty: Ty<'tcx>,
853-
ast_bounds: &[hir::GenericBound<'_>],
859+
ast_bounds: &[&hir::GenericBound<'_>],
854860
bounds: &mut Bounds<'tcx>,
855861
) {
856862
let constness = self.default_constness_for_trait_bounds();
@@ -865,7 +871,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
865871
hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => {}
866872
hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => self
867873
.instantiate_lang_item_trait_ref(
868-
lang_item, span, hir_id, args, param_ty, bounds,
874+
*lang_item, *span, *hir_id, args, param_ty, bounds,
869875
),
870876
hir::GenericBound::Outlives(ref l) => bounds
871877
.region_bounds
@@ -896,6 +902,42 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
896902
ast_bounds: &[hir::GenericBound<'_>],
897903
sized_by_default: SizedByDefault,
898904
span: Span,
905+
) -> Bounds<'tcx> {
906+
let ast_bounds: Vec<_> = ast_bounds.iter().collect();
907+
self.compute_bounds_inner(param_ty, &ast_bounds, sized_by_default, span)
908+
}
909+
910+
/// Convert the bounds in `ast_bounds` that refer to traits which define an associated type
911+
/// named `assoc_name` into ty::Bounds. Ignore the rest.
912+
pub fn compute_bounds_that_match_assoc_type(
913+
&self,
914+
param_ty: Ty<'tcx>,
915+
ast_bounds: &[hir::GenericBound<'_>],
916+
sized_by_default: SizedByDefault,
917+
span: Span,
918+
assoc_name: Ident,
919+
) -> Bounds<'tcx> {
920+
let mut result = Vec::new();
921+
922+
for ast_bound in ast_bounds {
923+
if let Some(trait_ref) = ast_bound.trait_ref() {
924+
if let Some(trait_did) = trait_ref.trait_def_id() {
925+
if self.tcx().trait_may_define_assoc_type(trait_did, assoc_name) {
926+
result.push(ast_bound);
927+
}
928+
}
929+
}
930+
}
931+
932+
self.compute_bounds_inner(param_ty, &result, sized_by_default, span)
933+
}
934+
935+
fn compute_bounds_inner(
936+
&self,
937+
param_ty: Ty<'tcx>,
938+
ast_bounds: &[&hir::GenericBound<'_>],
939+
sized_by_default: SizedByDefault,
940+
span: Span,
899941
) -> Bounds<'tcx> {
900942
let mut bounds = Bounds::default();
901943

@@ -1098,7 +1140,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10981140
// parameter to have a skipped binder.
10991141
let param_ty =
11001142
tcx.mk_projection(assoc_ty.def_id, projection_ty.skip_binder().substs);
1101-
self.add_bounds(param_ty, ast_bounds, bounds);
1143+
let ast_bounds: Vec<_> = ast_bounds.iter().collect();
1144+
self.add_bounds(param_ty, &ast_bounds, bounds);
11021145
}
11031146
}
11041147
Ok(())
@@ -1413,21 +1456,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
14131456
ty_param_def_id, assoc_name, span,
14141457
);
14151458

1416-
let predicates =
1417-
&self.get_type_parameter_bounds(span, ty_param_def_id.to_def_id()).predicates;
1459+
let predicates = &self
1460+
.get_type_parameter_bounds(span, ty_param_def_id.to_def_id(), assoc_name)
1461+
.predicates;
14181462

14191463
debug!("find_bound_for_assoc_item: predicates={:#?}", predicates);
14201464

14211465
let param_hir_id = tcx.hir().local_def_id_to_hir_id(ty_param_def_id);
14221466
let param_name = tcx.hir().ty_param_name(param_hir_id);
14231467
self.one_bound_for_assoc_type(
14241468
|| {
1425-
traits::transitive_bounds(
1469+
traits::transitive_bounds_that_define_assoc_type(
14261470
tcx,
14271471
predicates.iter().filter_map(|(p, _)| {
14281472
p.to_opt_poly_trait_ref().map(|trait_ref| trait_ref.value)
14291473
}),
1474+
assoc_name,
14301475
)
1476+
.into_iter()
14311477
},
14321478
|| param_name.to_string(),
14331479
assoc_name,

compiler/rustc_typeck/src/check/fn_ctxt/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_middle::ty::fold::TypeFoldable;
2020
use rustc_middle::ty::subst::GenericArgKind;
2121
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
2222
use rustc_session::Session;
23+
use rustc_span::symbol::Ident;
2324
use rustc_span::{self, Span};
2425
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
2526

@@ -183,7 +184,12 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
183184
}
184185
}
185186

186-
fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
187+
fn get_type_parameter_bounds(
188+
&self,
189+
_: Span,
190+
def_id: DefId,
191+
_: Ident,
192+
) -> ty::GenericPredicates<'tcx> {
187193
let tcx = self.tcx;
188194
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
189195
let item_id = tcx.hir().ty_param_owner(hir_id);

0 commit comments

Comments
 (0)