Skip to content

Commit 3d81698

Browse files
committed
Fix exponential blowup on nested types
1 parent 253ebd1 commit 3d81698

File tree

2 files changed

+94
-75
lines changed

2 files changed

+94
-75
lines changed

Diff for: 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
}

Diff for: src/librustc/traits/project.rs

+91-72
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,7 +111,7 @@ 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

116117
struct ProjectionTyCandidateSet<'tcx> {
@@ -818,45 +819,57 @@ fn project_type<'cx, 'gcx, 'tcx>(
818819
&obligation_trait_ref,
819820
&mut candidates);
820821

822+
let decide_commit = |candidates: &mut ProjectionTyCandidateSet<'tcx>| {
823+
debug!("{} candidates, ambiguous={}",
824+
candidates.vec.len(),
825+
candidates.ambiguous);
826+
827+
// Prefer where-clauses. As in select, if there are multiple
828+
// candidates, we prefer where-clause candidates over impls. This
829+
// may seem a bit surprising, since impls are the source of
830+
// "truth" in some sense, but in fact some of the impls that SEEM
831+
// applicable are not, because of nested obligations. Where
832+
// clauses are the safer choice. See the comment on
833+
// `select::SelectionCandidate` and #21974 for more details.
834+
if candidates.vec.len() > 1 {
835+
debug!("retaining param-env candidates only from {:?}", candidates.vec);
836+
candidates.vec.retain(|c| match *c {
837+
ProjectionTyCandidate::ParamEnv(..) => true,
838+
ProjectionTyCandidate::TraitDef(..) |
839+
ProjectionTyCandidate::Select(..) => false,
840+
});
841+
debug!("resulting candidate set: {:?}", candidates.vec);
842+
if candidates.vec.len() != 1 {
843+
candidates.ambiguous = true;
844+
return false;
845+
}
846+
}
847+
848+
assert!(candidates.vec.len() <= 1);
849+
match candidates.vec.get(0) {
850+
Some(&ProjectionTyCandidate::Select(..)) => true,
851+
_ => false,
852+
}
853+
};
854+
855+
// Note that at here, if `ProjectionTyCandidate::Select` is not going to be
856+
// a valid candidate, the closure is not executed at all. There are two cases,
857+
// one being trait selection error, and another being ambiguous candidates.
858+
// We handle both by the early return below.
821859
if let Err(e) = assemble_candidates_from_impls(selcx,
822860
obligation,
823861
&obligation_trait_ref,
824-
&mut candidates) {
862+
&mut candidates,
863+
decide_commit) {
825864
return Err(ProjectionTyError::TraitSelectionError(e));
826865
}
827866

828-
debug!("{} candidates, ambiguous={}",
829-
candidates.vec.len(),
830-
candidates.ambiguous);
831-
832867
// Inherent ambiguity that prevents us from even enumerating the
833868
// candidates.
834869
if candidates.ambiguous {
835870
return Err(ProjectionTyError::TooManyCandidates);
836871
}
837872

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);
859-
860873
match candidates.vec.pop() {
861874
Some(candidate) => {
862875
Ok(ProjectedTy::Progress(
@@ -952,7 +965,7 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
952965
debug!("assemble_candidates_from_predicates: predicate={:?}",
953966
predicate);
954967
match predicate {
955-
ty::Predicate::Projection(ref data) => {
968+
ty::Predicate::Projection(data) => {
956969
let same_def_id =
957970
data.0.projection_ty.item_def_id == obligation.predicate.item_def_id;
958971

@@ -975,26 +988,33 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
975988
data, is_match, same_def_id);
976989

977990
if is_match {
978-
candidate_set.vec.push(ctor(data.clone()));
991+
candidate_set.vec.push(ctor(data));
979992
}
980993
}
981-
_ => { }
994+
_ => {}
982995
}
983996
}
984997
}
985998

986-
fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
999+
enum NoImplCandidateError<'tcx> {
1000+
CanceledCommit,
1001+
SelectionError(SelectionError<'tcx>),
1002+
}
1003+
1004+
fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx,
1005+
F: FnOnce(&mut ProjectionTyCandidateSet<'tcx>) -> bool>(
9871006
selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
9881007
obligation: &ProjectionTyObligation<'tcx>,
9891008
obligation_trait_ref: &ty::TraitRef<'tcx>,
990-
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
1009+
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
1010+
decide_commit: F)
9911011
-> Result<(), SelectionError<'tcx>>
9921012
{
9931013
// If we are resolving `<T as TraitRef<...>>::Item == Type`,
9941014
// start out by selecting the predicate `T as TraitRef<...>`:
9951015
let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
9961016
let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate());
997-
selcx.infcx().probe(|_| {
1017+
match selcx.infcx().commit_if_ok(|_| {
9981018
let vtable = match selcx.select(&trait_obligation) {
9991019
Ok(Some(vtable)) => vtable,
10001020
Ok(None) => {
@@ -1004,21 +1024,20 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
10041024
Err(e) => {
10051025
debug!("assemble_candidates_from_impls: selection error {:?}",
10061026
e);
1007-
return Err(e);
1027+
return Err(NoImplCandidateError::SelectionError(e));
10081028
}
10091029
};
10101030

1011-
match vtable {
1012-
super::VtableClosure(_) |
1013-
super::VtableGenerator(_) |
1014-
super::VtableFnPointer(_) |
1015-
super::VtableObject(_) => {
1031+
let eligible = match &vtable {
1032+
&super::VtableClosure(_) |
1033+
&super::VtableGenerator(_) |
1034+
&super::VtableFnPointer(_) |
1035+
&super::VtableObject(_) => {
10161036
debug!("assemble_candidates_from_impls: vtable={:?}",
10171037
vtable);
1018-
1019-
candidate_set.vec.push(ProjectionTyCandidate::Select);
1038+
true
10201039
}
1021-
super::VtableImpl(ref impl_data) => {
1040+
&super::VtableImpl(ref impl_data) => {
10221041
// We have to be careful when projecting out of an
10231042
// impl because of specialization. If we are not in
10241043
// trans (i.e., projection mode is not "any"), and the
@@ -1062,29 +1081,27 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
10621081
node_item.item.defaultness.has_value()
10631082
} else {
10641083
node_item.item.defaultness.is_default() ||
1065-
selcx.tcx().impl_is_default(node_item.node.def_id())
1084+
selcx.tcx().impl_is_default(node_item.node.def_id())
10661085
};
10671086

10681087
// Only reveal a specializable default if we're past type-checking
10691088
// and the obligations is monomorphic, otherwise passes such as
10701089
// transmute checking and polymorphic MIR optimizations could
10711090
// get a result which isn't correct for all monomorphizations.
1072-
let new_candidate = if !is_default {
1073-
Some(ProjectionTyCandidate::Select)
1091+
if !is_default {
1092+
true
10741093
} else if obligation.param_env.reveal == Reveal::All {
10751094
assert!(!poly_trait_ref.needs_infer());
10761095
if !poly_trait_ref.needs_subst() {
1077-
Some(ProjectionTyCandidate::Select)
1096+
true
10781097
} else {
1079-
None
1098+
false
10801099
}
10811100
} else {
1082-
None
1083-
};
1084-
1085-
candidate_set.vec.extend(new_candidate);
1101+
false
1102+
}
10861103
}
1087-
super::VtableParam(..) => {
1104+
&super::VtableParam(..) => {
10881105
// This case tell us nothing about the value of an
10891106
// associated type. Consider:
10901107
//
@@ -1110,19 +1127,32 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
11101127
// in the compiler: a trait predicate (`T : SomeTrait`) and a
11111128
// projection. And the projection where clause is handled
11121129
// in `assemble_candidates_from_param_env`.
1130+
false
11131131
}
1114-
super::VtableAutoImpl(..) |
1115-
super::VtableBuiltin(..) => {
1132+
&super::VtableAutoImpl(..) |
1133+
&super::VtableBuiltin(..) => {
11161134
// These traits have no associated types.
11171135
span_bug!(
11181136
obligation.cause.span,
11191137
"Cannot project an associated type from `{:?}`",
11201138
vtable);
11211139
}
1140+
};
1141+
1142+
if eligible {
1143+
candidate_set.vec.push(ProjectionTyCandidate::Select(vtable));
11221144
}
11231145

1124-
Ok(())
1125-
})
1146+
if decide_commit(candidate_set) {
1147+
Ok(())
1148+
} else {
1149+
Err(NoImplCandidateError::CanceledCommit)
1150+
}
1151+
}) {
1152+
Ok(()) => Ok(()),
1153+
Err(NoImplCandidateError::CanceledCommit) => Ok(()),
1154+
Err(NoImplCandidateError::SelectionError(e)) => Err(e),
1155+
}
11261156
}
11271157

11281158
fn confirm_candidate<'cx, 'gcx, 'tcx>(
@@ -1142,30 +1172,19 @@ fn confirm_candidate<'cx, 'gcx, 'tcx>(
11421172
confirm_param_env_candidate(selcx, obligation, poly_projection)
11431173
}
11441174

1145-
ProjectionTyCandidate::Select => {
1146-
confirm_select_candidate(selcx, obligation, obligation_trait_ref)
1175+
ProjectionTyCandidate::Select(vtable) => {
1176+
confirm_select_candidate(selcx, obligation, obligation_trait_ref, vtable)
11471177
}
11481178
}
11491179
}
11501180

11511181
fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
11521182
selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
11531183
obligation: &ProjectionTyObligation<'tcx>,
1154-
obligation_trait_ref: &ty::TraitRef<'tcx>)
1184+
obligation_trait_ref: &ty::TraitRef<'tcx>,
1185+
vtable: Selection<'tcx>)
11551186
-> Progress<'tcx>
11561187
{
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-
11691188
match vtable {
11701189
super::VtableImpl(data) =>
11711190
confirm_impl_candidate(selcx, obligation, data),

0 commit comments

Comments
 (0)