Skip to content

Commit 596d6c4

Browse files
committed
Avoid cycle with projections from object types
Normalizing `<dyn Iterator<Item = ()> as Iterator>::Item` no longer requires selecting `dyn Iterator<Item = ()>: Iterator`. This was previously worked around by using a special type-folder to normalize things.
1 parent 34e5a49 commit 596d6c4

File tree

11 files changed

+193
-350
lines changed

11 files changed

+193
-350
lines changed

compiler/rustc_trait_selection/src/traits/project.rs

Lines changed: 77 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Code for projecting associated types out of trait references.
22
3-
use super::elaborate_predicates;
43
use super::specialization_graph;
54
use super::translate_substs;
65
use super::util;
@@ -53,13 +52,16 @@ pub enum ProjectionTyError<'tcx> {
5352

5453
#[derive(PartialEq, Eq, Debug)]
5554
enum ProjectionTyCandidate<'tcx> {
56-
// from a where-clause in the env or object type
55+
/// From a where-clause in the env or object type
5756
ParamEnv(ty::PolyProjectionPredicate<'tcx>),
5857

59-
// from the definition of `Trait` when you have something like <<A as Trait>::B as Trait2>::C
58+
/// From the definition of `Trait` when you have something like <<A as Trait>::B as Trait2>::C
6059
TraitDef(ty::PolyProjectionPredicate<'tcx>),
6160

62-
// from a "impl" (or a "pseudo-impl" returned by select)
61+
/// Bounds specified on an object type
62+
Object(ty::PolyProjectionPredicate<'tcx>),
63+
64+
/// From a "impl" (or a "pseudo-impl" returned by select)
6365
Select(Selection<'tcx>),
6466
}
6567

@@ -561,14 +563,6 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
561563
} else {
562564
obligations.extend(ty.obligations);
563565
}
564-
565-
obligations.push(get_paranoid_cache_value_obligation(
566-
infcx,
567-
param_env,
568-
projection_ty,
569-
cause,
570-
depth,
571-
));
572566
return Ok(Some(ty.value));
573567
}
574568
Err(ProjectionCacheEntry::Error) => {
@@ -703,45 +697,6 @@ fn prune_cache_value_obligations<'a, 'tcx>(
703697
NormalizedTy { value: result.value, obligations }
704698
}
705699

706-
/// Whenever we give back a cache result for a projection like `<T as
707-
/// Trait>::Item ==> X`, we *always* include the obligation to prove
708-
/// that `T: Trait` (we may also include some other obligations). This
709-
/// may or may not be necessary -- in principle, all the obligations
710-
/// that must be proven to show that `T: Trait` were also returned
711-
/// when the cache was first populated. But there are some vague concerns,
712-
/// and so we take the precautionary measure of including `T: Trait` in
713-
/// the result:
714-
///
715-
/// Concern #1. The current setup is fragile. Perhaps someone could
716-
/// have failed to prove the concerns from when the cache was
717-
/// populated, but also not have used a snapshot, in which case the
718-
/// cache could remain populated even though `T: Trait` has not been
719-
/// shown. In this case, the "other code" is at fault -- when you
720-
/// project something, you are supposed to either have a snapshot or
721-
/// else prove all the resulting obligations -- but it's still easy to
722-
/// get wrong.
723-
///
724-
/// Concern #2. Even within the snapshot, if those original
725-
/// obligations are not yet proven, then we are able to do projections
726-
/// that may yet turn out to be wrong. This *may* lead to some sort
727-
/// of trouble, though we don't have a concrete example of how that
728-
/// can occur yet. But it seems risky at best.
729-
fn get_paranoid_cache_value_obligation<'a, 'tcx>(
730-
infcx: &'a InferCtxt<'a, 'tcx>,
731-
param_env: ty::ParamEnv<'tcx>,
732-
projection_ty: ty::ProjectionTy<'tcx>,
733-
cause: ObligationCause<'tcx>,
734-
depth: usize,
735-
) -> PredicateObligation<'tcx> {
736-
let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
737-
Obligation {
738-
cause,
739-
recursion_depth: depth,
740-
param_env,
741-
predicate: trait_ref.without_const().to_predicate(infcx.tcx),
742-
}
743-
}
744-
745700
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
746701
/// hold. In various error cases, we cannot generate a valid
747702
/// normalized projection. Therefore, we create an inference variable
@@ -848,12 +803,21 @@ fn project_type<'cx, 'tcx>(
848803

849804
assemble_candidates_from_trait_def(selcx, obligation, &obligation_trait_ref, &mut candidates);
850805

851-
assemble_candidates_from_impls(selcx, obligation, &obligation_trait_ref, &mut candidates);
806+
assemble_candidates_from_object_ty(selcx, obligation, &obligation_trait_ref, &mut candidates);
807+
808+
if let ProjectionTyCandidateSet::Single(ProjectionTyCandidate::Object(_)) = candidates {
809+
// Avoid normalization cycle from selection (see
810+
// `assemble_candidates_from_object_ty`).
811+
// FIXME(lazy_normalization): Lazy normalization should save us from
812+
// having to do special case this.
813+
} else {
814+
assemble_candidates_from_impls(selcx, obligation, &obligation_trait_ref, &mut candidates);
815+
};
852816

853817
match candidates {
854-
ProjectionTyCandidateSet::Single(candidate) => Ok(ProjectedTy::Progress(
855-
confirm_candidate(selcx, obligation, &obligation_trait_ref, candidate),
856-
)),
818+
ProjectionTyCandidateSet::Single(candidate) => {
819+
Ok(ProjectedTy::Progress(confirm_candidate(selcx, obligation, candidate)))
820+
}
857821
ProjectionTyCandidateSet::None => Ok(ProjectedTy::NoProgress(
858822
selcx
859823
.tcx()
@@ -932,6 +896,53 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
932896
)
933897
}
934898

899+
/// In the case of a trait object like
900+
/// `<dyn Iterator<Item = ()> as Iterator>::Item` we can use the existential
901+
/// predicate in the trait object.
902+
///
903+
/// We don't go through the select candidate for these bounds to avoid cycles:
904+
/// In the above case, `dyn Iterator<Item = ()>: Iterator` would create a
905+
/// nested obligation of `<dyn Iterator<Item = ()> as Iterator>::Item: Sized`,
906+
/// this then has to be normalized without having to prove
907+
/// `dyn Iterator<Item = ()>: Iterator` again.
908+
fn assemble_candidates_from_object_ty<'cx, 'tcx>(
909+
selcx: &mut SelectionContext<'cx, 'tcx>,
910+
obligation: &ProjectionTyObligation<'tcx>,
911+
obligation_trait_ref: &ty::TraitRef<'tcx>,
912+
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
913+
) {
914+
debug!("assemble_candidates_from_object_ty(..)");
915+
916+
let tcx = selcx.tcx();
917+
918+
let self_ty = obligation_trait_ref.self_ty();
919+
let object_ty = selcx.infcx().shallow_resolve(self_ty);
920+
let data = match object_ty.kind {
921+
ty::Dynamic(ref data, ..) => data,
922+
ty::Infer(ty::TyVar(_)) => {
923+
// If the self-type is an inference variable, then it MAY wind up
924+
// being an object type, so induce an ambiguity.
925+
candidate_set.mark_ambiguous();
926+
return;
927+
}
928+
_ => return,
929+
};
930+
let env_predicates = data
931+
.projection_bounds()
932+
.filter(|bound| bound.item_def_id() == obligation.predicate.item_def_id)
933+
.map(|p| p.with_self_ty(tcx, object_ty).to_predicate(tcx));
934+
935+
assemble_candidates_from_predicates(
936+
selcx,
937+
obligation,
938+
obligation_trait_ref,
939+
candidate_set,
940+
ProjectionTyCandidate::Object,
941+
env_predicates,
942+
false,
943+
);
944+
}
945+
935946
fn assemble_candidates_from_predicates<'cx, 'tcx>(
936947
selcx: &mut SelectionContext<'cx, 'tcx>,
937948
obligation: &ProjectionTyObligation<'tcx>,
@@ -1000,7 +1011,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
10001011
super::ImplSource::Closure(_)
10011012
| super::ImplSource::Generator(_)
10021013
| super::ImplSource::FnPointer(_)
1003-
| super::ImplSource::Object(_)
10041014
| super::ImplSource::TraitAlias(_) => {
10051015
debug!("assemble_candidates_from_impls: impl_source={:?}", impl_source);
10061016
true
@@ -1125,6 +1135,12 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
11251135
// in `assemble_candidates_from_param_env`.
11261136
false
11271137
}
1138+
super::ImplSource::Object(_) => {
1139+
// Handled by the `Object` projection candidate. See
1140+
// `assemble_candidates_from_object_ty` for an explanation of
1141+
// why we special case object types.
1142+
false
1143+
}
11281144
super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) => {
11291145
// These traits have no associated types.
11301146
selcx.tcx().sess.delay_span_bug(
@@ -1150,13 +1166,13 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
11501166
fn confirm_candidate<'cx, 'tcx>(
11511167
selcx: &mut SelectionContext<'cx, 'tcx>,
11521168
obligation: &ProjectionTyObligation<'tcx>,
1153-
obligation_trait_ref: &ty::TraitRef<'tcx>,
11541169
candidate: ProjectionTyCandidate<'tcx>,
11551170
) -> Progress<'tcx> {
11561171
debug!("confirm_candidate(candidate={:?}, obligation={:?})", candidate, obligation);
11571172

11581173
let mut progress = match candidate {
1159-
ProjectionTyCandidate::ParamEnv(poly_projection) => {
1174+
ProjectionTyCandidate::ParamEnv(poly_projection)
1175+
| ProjectionTyCandidate::Object(poly_projection) => {
11601176
confirm_param_env_candidate(selcx, obligation, poly_projection, false)
11611177
}
11621178

@@ -1165,7 +1181,7 @@ fn confirm_candidate<'cx, 'tcx>(
11651181
}
11661182

11671183
ProjectionTyCandidate::Select(impl_source) => {
1168-
confirm_select_candidate(selcx, obligation, obligation_trait_ref, impl_source)
1184+
confirm_select_candidate(selcx, obligation, impl_source)
11691185
}
11701186
};
11711187
// When checking for cycle during evaluation, we compare predicates with
@@ -1182,7 +1198,6 @@ fn confirm_candidate<'cx, 'tcx>(
11821198
fn confirm_select_candidate<'cx, 'tcx>(
11831199
selcx: &mut SelectionContext<'cx, 'tcx>,
11841200
obligation: &ProjectionTyObligation<'tcx>,
1185-
obligation_trait_ref: &ty::TraitRef<'tcx>,
11861201
impl_source: Selection<'tcx>,
11871202
) -> Progress<'tcx> {
11881203
match impl_source {
@@ -1193,10 +1208,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
11931208
super::ImplSource::DiscriminantKind(data) => {
11941209
confirm_discriminant_kind_candidate(selcx, obligation, data)
11951210
}
1196-
super::ImplSource::Object(_) => {
1197-
confirm_object_candidate(selcx, obligation, obligation_trait_ref)
1198-
}
1199-
super::ImplSource::AutoImpl(..)
1211+
super::ImplSource::Object(_)
1212+
| super::ImplSource::AutoImpl(..)
12001213
| super::ImplSource::Param(..)
12011214
| super::ImplSource::Builtin(..)
12021215
| super::ImplSource::TraitAlias(..) =>
@@ -1211,72 +1224,6 @@ fn confirm_select_candidate<'cx, 'tcx>(
12111224
}
12121225
}
12131226

1214-
fn confirm_object_candidate<'cx, 'tcx>(
1215-
selcx: &mut SelectionContext<'cx, 'tcx>,
1216-
obligation: &ProjectionTyObligation<'tcx>,
1217-
obligation_trait_ref: &ty::TraitRef<'tcx>,
1218-
) -> Progress<'tcx> {
1219-
let self_ty = obligation_trait_ref.self_ty();
1220-
let object_ty = selcx.infcx().shallow_resolve(self_ty);
1221-
debug!("confirm_object_candidate(object_ty={:?})", object_ty);
1222-
let data = match object_ty.kind() {
1223-
ty::Dynamic(data, ..) => data,
1224-
_ => span_bug!(
1225-
obligation.cause.span,
1226-
"confirm_object_candidate called with non-object: {:?}",
1227-
object_ty
1228-
),
1229-
};
1230-
let env_predicates = data
1231-
.projection_bounds()
1232-
.map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate(selcx.tcx()));
1233-
let env_predicate = {
1234-
let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates);
1235-
1236-
// select only those projections that are actually projecting an
1237-
// item with the correct name
1238-
1239-
let env_predicates = env_predicates.filter_map(|o| match o.predicate.skip_binders() {
1240-
ty::PredicateAtom::Projection(data)
1241-
if data.projection_ty.item_def_id == obligation.predicate.item_def_id =>
1242-
{
1243-
Some(ty::Binder::bind(data))
1244-
}
1245-
_ => None,
1246-
});
1247-
1248-
// select those with a relevant trait-ref
1249-
let mut env_predicates = env_predicates.filter(|data| {
1250-
let data_poly_trait_ref = data.to_poly_trait_ref(selcx.tcx());
1251-
let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
1252-
selcx.infcx().probe(|_| {
1253-
selcx
1254-
.infcx()
1255-
.at(&obligation.cause, obligation.param_env)
1256-
.sup(obligation_poly_trait_ref, data_poly_trait_ref)
1257-
.is_ok()
1258-
})
1259-
});
1260-
1261-
// select the first matching one; there really ought to be one or
1262-
// else the object type is not WF, since an object type should
1263-
// include all of its projections explicitly
1264-
match env_predicates.next() {
1265-
Some(env_predicate) => env_predicate,
1266-
None => {
1267-
debug!(
1268-
"confirm_object_candidate: no env-predicate \
1269-
found in object type `{:?}`; ill-formed",
1270-
object_ty
1271-
);
1272-
return Progress::error(selcx.tcx());
1273-
}
1274-
}
1275-
};
1276-
1277-
confirm_param_env_candidate(selcx, obligation, env_predicate, false)
1278-
}
1279-
12801227
fn confirm_generator_candidate<'cx, 'tcx>(
12811228
selcx: &mut SelectionContext<'cx, 'tcx>,
12821229
obligation: &ProjectionTyObligation<'tcx>,

0 commit comments

Comments
 (0)