Skip to content

Commit 53bacac

Browse files
committed
Permit evaluation of assoc items on Self by avoiding cycle error
Skip the recursive evaluation of super-traits that reference associated types on `Self` directly (without using a fully-qualified path) in order to allow its use. The following code would previously emit a cycle evaluation error and will now successfuly compile: ```rust trait Bar<T> { fn foo() -> T; } trait Foo: Bar<Self::Qux> { type Qux; } ``` The following will also work: ```rust trait Bar<T> { fn foo() -> T; } trait Baz { type Qux; } trait Foo: Bar<Self::Qux> + Baz {} ``` Fix rust-lang#35237.
1 parent 85fbf49 commit 53bacac

File tree

11 files changed

+310
-65
lines changed

11 files changed

+310
-65
lines changed

compiler/rustc_infer/src/traits/util.rs

+29-3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ impl Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> {
7373
pub struct Elaborator<'tcx> {
7474
stack: Vec<PredicateObligation<'tcx>>,
7575
visited: PredicateSet<'tcx>,
76+
/// If this is `true` we will call `super_predicates_of_skip_self_param` instead of
77+
/// `super_predicates_of`, which lets us get around cycle errors when super-traits reference
78+
/// associated types on `Self`. See `super_predicates_of_skip_self_param` for more info.
79+
evaluate_self_param: bool,
7680
}
7781

7882
pub fn elaborate_trait_ref<'tcx>(
@@ -104,7 +108,7 @@ pub fn elaborate_obligations<'tcx>(
104108
) -> Elaborator<'tcx> {
105109
let mut visited = PredicateSet::new(tcx);
106110
obligations.retain(|obligation| visited.insert(obligation.predicate));
107-
Elaborator { stack: obligations, visited }
111+
Elaborator { stack: obligations, visited, evaluate_self_param: true }
108112
}
109113

110114
fn predicate_obligation<'tcx>(
@@ -131,9 +135,14 @@ impl Elaborator<'tcx> {
131135
match obligation.predicate.skip_binders() {
132136
ty::PredicateAtom::Trait(data, _) => {
133137
// Get predicates declared on the trait.
134-
let predicates = tcx.super_predicates_of(data.def_id());
138+
let predicates = if self.evaluate_self_param {
139+
tcx.super_predicates_of(data.def_id())
140+
} else {
141+
tcx.super_predicates_of_skip_self_param(data.def_id())
142+
}
143+
.predicates;
135144

136-
let obligations = predicates.predicates.iter().map(|&(pred, span)| {
145+
let obligations = predicates.iter().map(|&(pred, span)| {
137146
predicate_obligation(
138147
pred.subst_supertrait(tcx, &ty::Binder::bind(data.trait_ref)),
139148
Some(span),
@@ -271,6 +280,23 @@ pub fn supertraits<'tcx>(
271280
elaborate_trait_ref(tcx, trait_ref).filter_to_traits()
272281
}
273282

283+
/// Elaborate supertraits.
284+
///
285+
/// See `super_predicates_of_skip_self_param` for the behavior when `skip_self_param` is `true`.
286+
pub fn supertraits_maybe_skip_self_param_users<'tcx>(
287+
tcx: TyCtxt<'tcx>,
288+
bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
289+
skip_self_param: bool,
290+
) -> Supertraits<'tcx> {
291+
let mut obligations: Vec<_> = bounds
292+
.map(|trait_ref| predicate_obligation(trait_ref.without_const().to_predicate(tcx), None))
293+
.collect();
294+
let mut visited = PredicateSet::new(tcx);
295+
obligations.retain(|obligation| visited.insert(obligation.predicate));
296+
Elaborator { stack: obligations, visited, evaluate_self_param: !skip_self_param }
297+
.filter_to_traits()
298+
}
299+
274300
pub fn transitive_bounds<'tcx>(
275301
tcx: TyCtxt<'tcx>,
276302
bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,

compiler/rustc_middle/src/query/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,13 @@ rustc_queries! {
357357
desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) }
358358
}
359359

360+
query super_predicates_of_skip_self_param(key: DefId) -> ty::GenericPredicates<'tcx> {
361+
desc {
362+
|tcx| "computing the supertraits of `{}` skipping super-traits referencing `Self`",
363+
tcx.def_path_str(key)
364+
}
365+
}
366+
360367
/// To avoid cycles within the predicates of a single item we compute
361368
/// per-type-parameter predicates for resolving `T::AssocTy`.
362369
query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> {

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, supertraits_maybe_skip_self_param_users, transitive_bounds,
69+
SupertraitDefIds, Supertraits,
6970
};
7071

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

compiler/rustc_typeck/src/astconv/mod.rs

+136-37
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ use rustc_errors::{struct_span_err, Applicability, ErrorReported, FatalError};
1515
use rustc_hir as hir;
1616
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
1717
use rustc_hir::def_id::{DefId, LocalDefId};
18-
use rustc_hir::intravisit::{walk_generics, Visitor as _};
18+
use rustc_hir::intravisit::{self, walk_generics, Visitor};
1919
use rustc_hir::lang_items::LangItem;
2020
use rustc_hir::{Constness, GenericArg, GenericArgs};
2121
use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef};
2222
use rustc_middle::ty::GenericParamDefKind;
2323
use rustc_middle::ty::{self, Const, DefIdTree, Ty, TyCtxt, TypeFoldable};
2424
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
25-
use rustc_span::symbol::{Ident, Symbol};
25+
use rustc_span::symbol::{kw, Ident, Symbol};
2626
use rustc_span::{Span, DUMMY_SP};
2727
use rustc_target::spec::abi;
2828
use rustc_trait_selection::traits;
@@ -285,8 +285,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
285285
// region with the current anon region binding (in other words,
286286
// whatever & would get replaced with).
287287
debug!(
288-
"create_substs_for_ast_path(def_id={:?}, self_ty={:?}, \
289-
generic_args={:?})",
288+
"create_substs_for_ast_path(def_id={:?}, self_ty={:?}, generic_args={:?})",
290289
def_id, self_ty, generic_args
291290
);
292291

@@ -316,7 +315,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
316315
infer_args,
317316
);
318317

319-
let is_object = self_ty.map_or(false, |ty| ty == self.tcx().types.trait_object_dummy_self);
318+
let is_object = self_ty.map_or(false, |ty| ty == tcx.types.trait_object_dummy_self);
320319
let default_needs_object_self = |param: &ty::GenericParamDef| {
321320
if let GenericParamDefKind::Type { has_default, .. } = param.kind {
322321
if is_object && has_default {
@@ -356,14 +355,42 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
356355
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
357356
self.ast_region_to_region(&lt, Some(param)).into()
358357
}
359-
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
360-
if let (hir::TyKind::Infer, false) = (&ty.kind, self.allow_ty_infer()) {
358+
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => match &ty.kind {
359+
hir::TyKind::Infer if !self.allow_ty_infer() => {
361360
inferred_params.push(ty.span);
362361
tcx.ty_error().into()
363-
} else {
364-
self.ast_ty_to_ty(&ty).into()
365362
}
366-
}
363+
hir::TyKind::Path(hir::QPath::TypeRelative(
364+
hir::Ty {
365+
kind:
366+
hir::TyKind::Path(
367+
hir::QPath::Resolved(
368+
None,
369+
hir::Path { res: res @ Res::SelfTy(Some(_), None), .. },
370+
),
371+
..,
372+
),
373+
..
374+
},
375+
segment,
376+
)) if Some(tcx.types.self_param) == self_ty => {
377+
// Handle `trait A: B<Self::C> { type C; }` to avoid cycle error.
378+
// See `super_predicates_of_skip_self_param` for more information.
379+
self.associated_path_to_ty(
380+
ty.hir_id,
381+
ty.span,
382+
tcx.types.self_param,
383+
*res,
384+
segment,
385+
false,
386+
true,
387+
)
388+
.map(|(ty, _, _)| ty)
389+
.unwrap_or_else(|_| tcx.ty_error())
390+
.into()
391+
}
392+
_ => self.ast_ty_to_ty(&ty).into(),
393+
},
367394
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
368395
ty::Const::from_opt_const_arg_anon_const(
369396
tcx,
@@ -527,7 +554,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
527554
) -> GenericArgCountResult {
528555
let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());
529556

530-
debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id);
557+
debug!(
558+
"instantiate_poly_trait_ref({:?}, def_id={:?}, self_ty={:?})",
559+
trait_ref, trait_def_id, self_ty
560+
);
531561

532562
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1);
533563

@@ -704,9 +734,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
704734
if tpb.path.res != Res::Def(DefKind::Trait, kind_id) {
705735
tcx.sess.span_warn(
706736
span,
707-
"default bound relaxed for a type parameter, but \
708-
this does nothing because the given bound is not \
709-
a default; only `?Sized` is supported",
737+
"default bound relaxed for a type parameter, but this does nothing \
738+
because the given bound is not a default; only `?Sized` is supported",
710739
);
711740
}
712741
}
@@ -741,15 +770,61 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
741770
param_ty: Ty<'tcx>,
742771
ast_bounds: &[hir::GenericBound<'_>],
743772
bounds: &mut Bounds<'tcx>,
773+
skip_self_param_evaluation: bool,
744774
) {
745775
let mut trait_bounds = Vec::new();
746776
let mut region_bounds = Vec::new();
747777

778+
/// Walk each bound to see if they reference an associated type of `Self` without
779+
/// specifying a trait (using a Resolved path `Self::Foo` instead of fully qualified path
780+
/// `<Self as Bar>::Foo`) and if so, skip them if `skip_self_param_evaluation` is `true`.
781+
/// This way, we avoid cycle errors and are able to resolve the associated type
782+
/// successfully.
783+
struct SelfFinder(bool);
784+
785+
impl<'v> Visitor<'v> for SelfFinder {
786+
type Map = intravisit::ErasedMap<'v>;
787+
788+
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
789+
intravisit::NestedVisitorMap::None
790+
}
791+
792+
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
793+
if let hir::TyKind::Path(hir::QPath::TypeRelative(
794+
hir::Ty {
795+
kind:
796+
hir::TyKind::Path(
797+
hir::QPath::Resolved(
798+
None,
799+
hir::Path { res: Res::SelfTy(Some(_), None), .. },
800+
),
801+
..,
802+
),
803+
..
804+
},
805+
_,
806+
)) = t.kind
807+
{
808+
self.0 = true;
809+
}
810+
intravisit::walk_ty(self, t)
811+
}
812+
}
813+
748814
let constness = self.default_constness_for_trait_bounds();
749815
for ast_bound in ast_bounds {
750816
match *ast_bound {
751-
hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::None) => {
752-
trait_bounds.push((b, constness))
817+
hir::GenericBound::Trait(ref b, modif @ hir::TraitBoundModifier::None) => {
818+
let mut visitor = SelfFinder(false);
819+
visitor.visit_poly_trait_ref(b, modif);
820+
821+
let is_self_param = match param_ty.kind {
822+
ty::Param(param) if param.name == kw::SelfUpper => true,
823+
_ => false,
824+
};
825+
if !(visitor.0 && skip_self_param_evaluation && is_self_param) {
826+
trait_bounds.push((b, constness))
827+
}
753828
}
754829
hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::MaybeConst) => {
755830
trait_bounds.push((b, Constness::NotConst))
@@ -794,10 +869,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
794869
ast_bounds: &[hir::GenericBound<'_>],
795870
sized_by_default: SizedByDefault,
796871
span: Span,
872+
skip_self_param_evaluation: bool,
797873
) -> Bounds<'tcx> {
798874
let mut bounds = Bounds::default();
799875

800-
self.add_bounds(param_ty, ast_bounds, &mut bounds);
876+
self.add_bounds(param_ty, ast_bounds, &mut bounds, skip_self_param_evaluation);
801877
bounds.trait_bounds.sort_by_key(|(t, _, _)| t.def_id());
802878

803879
bounds.implicitly_sized = if let SizedByDefault::Yes = sized_by_default {
@@ -969,7 +1045,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
9691045
// Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
9701046
// parameter to have a skipped binder.
9711047
let param_ty = tcx.mk_projection(assoc_ty.def_id, candidate.skip_binder().substs);
972-
self.add_bounds(param_ty, ast_bounds, bounds);
1048+
self.add_bounds(param_ty, ast_bounds, bounds, false);
9731049
}
9741050
}
9751051
Ok(())
@@ -996,7 +1072,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
9961072

9971073
let mut bounds = Bounds::default();
9981074
let mut potential_assoc_types = Vec::new();
999-
let dummy_self = self.tcx().types.trait_object_dummy_self;
1075+
let dummy_self = tcx.types.trait_object_dummy_self;
10001076
for trait_bound in trait_bounds.iter().rev() {
10011077
if let GenericArgCountResult {
10021078
correct:
@@ -1274,15 +1350,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
12741350
err.emit();
12751351
}
12761352

1277-
// Search for a bound on a type parameter which includes the associated item
1278-
// given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter
1279-
// This function will fail if there are no suitable bounds or there is
1280-
// any ambiguity.
1353+
/// Search for a bound on a type parameter which includes the associated item
1354+
/// given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter
1355+
/// This function will fail if there are no suitable bounds or there is
1356+
/// any ambiguity.
1357+
///
1358+
/// See `super_predicates_of_skip_self_param` for the behavior when `skip_self_param_evaluation`
1359+
/// is `true`.
12811360
fn find_bound_for_assoc_item(
12821361
&self,
12831362
ty_param_def_id: LocalDefId,
12841363
assoc_name: Ident,
12851364
span: Span,
1365+
skip_self_param_evaluation: bool,
12861366
) -> Result<ty::PolyTraitRef<'tcx>, ErrorReported> {
12871367
let tcx = self.tcx();
12881368

@@ -1300,9 +1380,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
13001380
let param_name = tcx.hir().ty_param_name(param_hir_id);
13011381
self.one_bound_for_assoc_type(
13021382
|| {
1303-
traits::transitive_bounds(
1383+
traits::supertraits_maybe_skip_self_param_users(
13041384
tcx,
13051385
predicates.iter().filter_map(|(p, _)| p.to_opt_poly_trait_ref()),
1386+
skip_self_param_evaluation,
13061387
)
13071388
},
13081389
|| param_name.to_string(),
@@ -1432,12 +1513,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
14321513
Ok(bound)
14331514
}
14341515

1435-
// Create a type from a path to an associated type.
1436-
// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
1437-
// and item_segment is the path segment for `D`. We return a type and a def for
1438-
// the whole path.
1439-
// Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type
1440-
// parameter or `Self`.
1516+
/// Create a type from a path to an associated type.
1517+
/// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
1518+
/// and `item_segment` is the path segment for `D`. We return a type and a def for
1519+
/// the whole path.
1520+
/// Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type
1521+
/// parameter or `Self`.
1522+
///
1523+
/// When `dont_recurse` is `true`, and we are evaluation a path rooted in `Self`, we will skip
1524+
/// super-traits that reference `Self` to avoid cycle errors. See
1525+
/// `super_predicates_of_skip_self_param` for more information.
14411526
pub fn associated_path_to_ty(
14421527
&self,
14431528
hir_ref_id: hir::HirId,
@@ -1446,6 +1531,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
14461531
qself_res: Res,
14471532
assoc_segment: &hir::PathSegment<'_>,
14481533
permit_variants: bool,
1534+
dont_recurse: bool,
14491535
) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorReported> {
14501536
let tcx = self.tcx();
14511537
let assoc_ident = assoc_segment.ident;
@@ -1494,10 +1580,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
14941580
|| None,
14951581
)?
14961582
}
1497-
(
1498-
&ty::Param(_),
1499-
Res::SelfTy(Some(param_did), None) | Res::Def(DefKind::TyParam, param_did),
1500-
) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?,
1583+
(&ty::Param(_), Res::SelfTy(Some(param_did), None)) => self.find_bound_for_assoc_item(
1584+
param_did.expect_local(),
1585+
assoc_ident,
1586+
span,
1587+
dont_recurse,
1588+
)?,
1589+
(&ty::Param(_), Res::Def(DefKind::TyParam, param_did)) => {
1590+
self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span, false)?
1591+
}
15011592
_ => {
15021593
if variant_resolution.is_some() {
15031594
// Variant in type position
@@ -2036,9 +2127,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
20362127
} else {
20372128
Res::Err
20382129
};
2039-
self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, res, segment, false)
2040-
.map(|(ty, _, _)| ty)
2041-
.unwrap_or_else(|_| tcx.ty_error())
2130+
self.associated_path_to_ty(
2131+
ast_ty.hir_id,
2132+
ast_ty.span,
2133+
ty,
2134+
res,
2135+
segment,
2136+
false,
2137+
false,
2138+
)
2139+
.map(|(ty, _, _)| ty)
2140+
.unwrap_or_else(|_| tcx.ty_error())
20422141
}
20432142
hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => {
20442143
let def_id = tcx.require_lang_item(lang_item, Some(span));

0 commit comments

Comments
 (0)