Skip to content

Commit 619ad71

Browse files
committed
Fix exponential blowup on nested types
1 parent 93e6b0d commit 619ad71

File tree

2 files changed

+114
-101
lines changed

2 files changed

+114
-101
lines changed

src/librustc/traits/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ pub type SelectionResult<'tcx, T> = Result<Option<T>, SelectionError<'tcx>>;
304304
/// ### The type parameter `N`
305305
///
306306
/// See explanation on `VtableImplData`.
307-
#[derive(Clone, RustcEncodable, RustcDecodable)]
307+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
308308
pub enum Vtable<'tcx, N> {
309309
/// Vtable identifying a particular impl.
310310
VtableImpl(VtableImplData<'tcx, N>),
@@ -374,13 +374,13 @@ pub struct VtableClosureData<'tcx, N> {
374374
pub nested: Vec<N>
375375
}
376376

377-
#[derive(Clone, RustcEncodable, RustcDecodable)]
377+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
378378
pub struct VtableAutoImplData<N> {
379379
pub trait_def_id: DefId,
380380
pub nested: Vec<N>
381381
}
382382

383-
#[derive(Clone, RustcEncodable, RustcDecodable)]
383+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
384384
pub struct VtableBuiltinData<N> {
385385
pub nested: Vec<N>
386386
}

src/librustc/traits/project.rs

+111-98
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use super::translate_substs;
1616
use super::Obligation;
1717
use super::ObligationCause;
1818
use super::PredicateObligation;
19+
use super::Selection;
1920
use super::SelectionContext;
2021
use super::SelectionError;
2122
use super::VtableClosureData;
@@ -110,12 +111,59 @@ enum ProjectionTyCandidate<'tcx> {
110111
TraitDef(ty::PolyProjectionPredicate<'tcx>),
111112

112113
// from a "impl" (or a "pseudo-impl" returned by select)
113-
Select,
114+
Select(Selection<'tcx>),
114115
}
115116

116-
struct ProjectionTyCandidateSet<'tcx> {
117-
vec: Vec<ProjectionTyCandidate<'tcx>>,
118-
ambiguous: bool
117+
enum ProjectionTyCandidateSet<'tcx> {
118+
None,
119+
Single(ProjectionTyCandidate<'tcx>),
120+
Ambiguous,
121+
Error(SelectionError<'tcx>),
122+
}
123+
124+
impl<'tcx> ProjectionTyCandidateSet<'tcx> {
125+
fn mark_ambiguous(&mut self) {
126+
*self = ProjectionTyCandidateSet::Ambiguous;
127+
}
128+
129+
fn mark_error(&mut self, err: SelectionError<'tcx>) {
130+
*self = ProjectionTyCandidateSet::Error(err);
131+
}
132+
133+
// Returns true if the push was successful, or false if the candidate
134+
// was discarded -- this could be because of ambiguity, or because
135+
// a higher-priority candidate is already there.
136+
fn push_candidate(&mut self, candidate: ProjectionTyCandidate<'tcx>) -> bool {
137+
use self::ProjectionTyCandidateSet::*;
138+
use self::ProjectionTyCandidate::*;
139+
match self {
140+
None => {
141+
*self = Single(candidate);
142+
true
143+
}
144+
Single(current) => {
145+
// No duplicates are expected.
146+
assert_ne!(current, &candidate);
147+
// Prefer where-clauses. As in select, if there are multiple
148+
// candidates, we prefer where-clause candidates over impls. This
149+
// may seem a bit surprising, since impls are the source of
150+
// "truth" in some sense, but in fact some of the impls that SEEM
151+
// applicable are not, because of nested obligations. Where
152+
// clauses are the safer choice. See the comment on
153+
// `select::SelectionCandidate` and #21974 for more details.
154+
match (current, candidate) {
155+
(ParamEnv(..), ParamEnv(..)) => { *self = Ambiguous; }
156+
(ParamEnv(..), _) => {}
157+
(_, ParamEnv(..)) => { unreachable!(); }
158+
(_, _) => { *self = Ambiguous; }
159+
}
160+
false
161+
}
162+
Ambiguous | Error(..) => {
163+
false
164+
}
165+
}
166+
}
119167
}
120168

121169
/// Evaluates constraints of the form:
@@ -803,11 +851,11 @@ fn project_type<'cx, 'gcx, 'tcx>(
803851
return Ok(ProjectedTy::Progress(Progress::error(selcx.tcx())));
804852
}
805853

806-
let mut candidates = ProjectionTyCandidateSet {
807-
vec: Vec::new(),
808-
ambiguous: false,
809-
};
854+
let mut candidates = ProjectionTyCandidateSet::None;
810855

856+
// Make sure that the following procedures are kept in order. ParamEnv
857+
// needs to be first because it has highest priority, and Select checks
858+
// the return value of push_candidate which assumes it's ran at last.
811859
assemble_candidates_from_param_env(selcx,
812860
obligation,
813861
&obligation_trait_ref,
@@ -818,57 +866,27 @@ fn project_type<'cx, 'gcx, 'tcx>(
818866
&obligation_trait_ref,
819867
&mut candidates);
820868

821-
if let Err(e) = assemble_candidates_from_impls(selcx,
822-
obligation,
823-
&obligation_trait_ref,
824-
&mut candidates) {
825-
return Err(ProjectionTyError::TraitSelectionError(e));
826-
}
827-
828-
debug!("{} candidates, ambiguous={}",
829-
candidates.vec.len(),
830-
candidates.ambiguous);
831-
832-
// Inherent ambiguity that prevents us from even enumerating the
833-
// candidates.
834-
if candidates.ambiguous {
835-
return Err(ProjectionTyError::TooManyCandidates);
836-
}
837-
838-
// Prefer where-clauses. As in select, if there are multiple
839-
// candidates, we prefer where-clause candidates over impls. This
840-
// may seem a bit surprising, since impls are the source of
841-
// "truth" in some sense, but in fact some of the impls that SEEM
842-
// applicable are not, because of nested obligations. Where
843-
// clauses are the safer choice. See the comment on
844-
// `select::SelectionCandidate` and #21974 for more details.
845-
if candidates.vec.len() > 1 {
846-
debug!("retaining param-env candidates only from {:?}", candidates.vec);
847-
candidates.vec.retain(|c| match *c {
848-
ProjectionTyCandidate::ParamEnv(..) => true,
849-
ProjectionTyCandidate::TraitDef(..) |
850-
ProjectionTyCandidate::Select => false,
851-
});
852-
debug!("resulting candidate set: {:?}", candidates.vec);
853-
if candidates.vec.len() != 1 {
854-
return Err(ProjectionTyError::TooManyCandidates);
855-
}
856-
}
857-
858-
assert!(candidates.vec.len() <= 1);
869+
assemble_candidates_from_impls(selcx,
870+
obligation,
871+
&obligation_trait_ref,
872+
&mut candidates);
873+
874+
match candidates {
875+
ProjectionTyCandidateSet::Single(candidate) => Ok(ProjectedTy::Progress(
876+
confirm_candidate(selcx,
877+
obligation,
878+
&obligation_trait_ref,
879+
candidate))),
880+
ProjectionTyCandidateSet::None => Ok(ProjectedTy::NoProgress(
881+
selcx.tcx().mk_projection(
882+
obligation.predicate.item_def_id,
883+
obligation.predicate.substs))),
884+
// Error occurred while trying to processing impls.
885+
ProjectionTyCandidateSet::Error(e) => Err(ProjectionTyError::TraitSelectionError(e)),
886+
// Inherent ambiguity that prevents us from even enumerating the
887+
// candidates.
888+
ProjectionTyCandidateSet::Ambiguous => Err(ProjectionTyError::TooManyCandidates),
859889

860-
match candidates.vec.pop() {
861-
Some(candidate) => {
862-
Ok(ProjectedTy::Progress(
863-
confirm_candidate(selcx,
864-
obligation,
865-
&obligation_trait_ref,
866-
candidate)))
867-
}
868-
None => Ok(ProjectedTy::NoProgress(
869-
selcx.tcx().mk_projection(
870-
obligation.predicate.item_def_id,
871-
obligation.predicate.substs)))
872890
}
873891
}
874892

@@ -918,7 +936,7 @@ fn assemble_candidates_from_trait_def<'cx, 'gcx, 'tcx>(
918936
ty::TyInfer(ty::TyVar(_)) => {
919937
// If the self-type is an inference variable, then it MAY wind up
920938
// being a projected type, so induce an ambiguity.
921-
candidate_set.ambiguous = true;
939+
candidate_set.mark_ambiguous();
922940
return;
923941
}
924942
_ => { return; }
@@ -952,7 +970,7 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
952970
debug!("assemble_candidates_from_predicates: predicate={:?}",
953971
predicate);
954972
match predicate {
955-
ty::Predicate::Projection(ref data) => {
973+
ty::Predicate::Projection(data) => {
956974
let same_def_id =
957975
data.0.projection_ty.item_def_id == obligation.predicate.item_def_id;
958976

@@ -975,10 +993,10 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
975993
data, is_match, same_def_id);
976994

977995
if is_match {
978-
candidate_set.vec.push(ctor(data.clone()));
996+
candidate_set.push_candidate(ctor(data));
979997
}
980998
}
981-
_ => { }
999+
_ => {}
9821000
}
9831001
}
9841002
}
@@ -988,37 +1006,36 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
9881006
obligation: &ProjectionTyObligation<'tcx>,
9891007
obligation_trait_ref: &ty::TraitRef<'tcx>,
9901008
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
991-
-> Result<(), SelectionError<'tcx>>
9921009
{
9931010
// If we are resolving `<T as TraitRef<...>>::Item == Type`,
9941011
// start out by selecting the predicate `T as TraitRef<...>`:
9951012
let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
9961013
let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate());
997-
selcx.infcx().probe(|_| {
1014+
let _ = selcx.infcx().commit_if_ok(|_| {
9981015
let vtable = match selcx.select(&trait_obligation) {
9991016
Ok(Some(vtable)) => vtable,
10001017
Ok(None) => {
1001-
candidate_set.ambiguous = true;
1002-
return Ok(());
1018+
candidate_set.mark_ambiguous();
1019+
return Err(());
10031020
}
10041021
Err(e) => {
10051022
debug!("assemble_candidates_from_impls: selection error {:?}",
10061023
e);
1007-
return Err(e);
1024+
candidate_set.mark_error(e);
1025+
return Err(());
10081026
}
10091027
};
10101028

1011-
match vtable {
1029+
let eligible = match &vtable {
10121030
super::VtableClosure(_) |
10131031
super::VtableGenerator(_) |
10141032
super::VtableFnPointer(_) |
10151033
super::VtableObject(_) => {
10161034
debug!("assemble_candidates_from_impls: vtable={:?}",
10171035
vtable);
1018-
1019-
candidate_set.vec.push(ProjectionTyCandidate::Select);
1036+
true
10201037
}
1021-
super::VtableImpl(ref impl_data) => {
1038+
super::VtableImpl(impl_data) => {
10221039
// We have to be careful when projecting out of an
10231040
// impl because of specialization. If we are not in
10241041
// trans (i.e., projection mode is not "any"), and the
@@ -1062,27 +1079,25 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
10621079
node_item.item.defaultness.has_value()
10631080
} else {
10641081
node_item.item.defaultness.is_default() ||
1065-
selcx.tcx().impl_is_default(node_item.node.def_id())
1082+
selcx.tcx().impl_is_default(node_item.node.def_id())
10661083
};
10671084

10681085
// Only reveal a specializable default if we're past type-checking
10691086
// and the obligations is monomorphic, otherwise passes such as
10701087
// transmute checking and polymorphic MIR optimizations could
10711088
// get a result which isn't correct for all monomorphizations.
1072-
let new_candidate = if !is_default {
1073-
Some(ProjectionTyCandidate::Select)
1089+
if !is_default {
1090+
true
10741091
} else if obligation.param_env.reveal == Reveal::All {
10751092
assert!(!poly_trait_ref.needs_infer());
10761093
if !poly_trait_ref.needs_subst() {
1077-
Some(ProjectionTyCandidate::Select)
1094+
true
10781095
} else {
1079-
None
1096+
false
10801097
}
10811098
} else {
1082-
None
1083-
};
1084-
1085-
candidate_set.vec.extend(new_candidate);
1099+
false
1100+
}
10861101
}
10871102
super::VtableParam(..) => {
10881103
// This case tell us nothing about the value of an
@@ -1110,6 +1125,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
11101125
// in the compiler: a trait predicate (`T : SomeTrait`) and a
11111126
// projection. And the projection where clause is handled
11121127
// in `assemble_candidates_from_param_env`.
1128+
false
11131129
}
11141130
super::VtableAutoImpl(..) |
11151131
super::VtableBuiltin(..) => {
@@ -1119,10 +1135,18 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
11191135
"Cannot project an associated type from `{:?}`",
11201136
vtable);
11211137
}
1122-
}
1138+
};
11231139

1124-
Ok(())
1125-
})
1140+
if eligible {
1141+
if candidate_set.push_candidate(ProjectionTyCandidate::Select(vtable)) {
1142+
Ok(())
1143+
} else {
1144+
Err(())
1145+
}
1146+
} else {
1147+
Err(())
1148+
}
1149+
});
11261150
}
11271151

11281152
fn confirm_candidate<'cx, 'gcx, 'tcx>(
@@ -1142,30 +1166,19 @@ fn confirm_candidate<'cx, 'gcx, 'tcx>(
11421166
confirm_param_env_candidate(selcx, obligation, poly_projection)
11431167
}
11441168

1145-
ProjectionTyCandidate::Select => {
1146-
confirm_select_candidate(selcx, obligation, obligation_trait_ref)
1169+
ProjectionTyCandidate::Select(vtable) => {
1170+
confirm_select_candidate(selcx, obligation, obligation_trait_ref, vtable)
11471171
}
11481172
}
11491173
}
11501174

11511175
fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
11521176
selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
11531177
obligation: &ProjectionTyObligation<'tcx>,
1154-
obligation_trait_ref: &ty::TraitRef<'tcx>)
1178+
obligation_trait_ref: &ty::TraitRef<'tcx>,
1179+
vtable: Selection<'tcx>)
11551180
-> Progress<'tcx>
11561181
{
1157-
let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
1158-
let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate());
1159-
let vtable = match selcx.select(&trait_obligation) {
1160-
Ok(Some(vtable)) => vtable,
1161-
_ => {
1162-
span_bug!(
1163-
obligation.cause.span,
1164-
"Failed to select `{:?}`",
1165-
trait_obligation);
1166-
}
1167-
};
1168-
11691182
match vtable {
11701183
super::VtableImpl(data) =>
11711184
confirm_impl_candidate(selcx, obligation, data),

0 commit comments

Comments
 (0)