Skip to content

Commit 2727a8e

Browse files
committed
Auto merge of rust-lang#27984 - arielb1:misc-assemble-improvements, r=nikomatsakis
this resolves type-variables early in assemble_candidates and bails out quickly if the self type is an inference variable (which would fail anyway because of `assemble_candidates_from_projected_tys`). In both these cases, `assemble_candidates_from_impls` would try to go over all impls and match them, leading to O(`n*m`) performance. Fixing this improves rustc type-checking performance by 10%. As type-checking is only is 5% of compilation, this doesn't impact bootstrap times, but *does* improve type-error-detection time which is nice. Crates that have many dependencies and contain significant amounts of generic functions could see a bigger perf boost. As a microbenchmark, the crate generated by ``` echo '#![feature(rustc_private)]' echo 'extern crate rustc_driver;' for i in {1..1000}; do cat << _EOF_ pub fn foo$i<T>() { let mut v = Vec::new(); let _w = v.clone(); v.push(""); } _EOF_ done ``` sees performance improve from 7.2 to 1.4 seconds. I imagine many crates would fall somewhere in-between. r? @nikomatsakis
2 parents 1b908be + ab86bf5 commit 2727a8e

File tree

1 file changed

+36
-31
lines changed

1 file changed

+36
-31
lines changed

src/librustc/middle/traits/select.rs

+36-31
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ pub enum MethodMatchedData {
186186
/// that we can have both a projection candidate and a where-clause candidate
187187
/// for the same obligation. In that case either would do (except that
188188
/// different "leaps of logic" would occur if inference variables are
189-
/// present), and we just pick the projection. This is, for example,
189+
/// present), and we just pick the where-clause. This is, for example,
190190
/// required for associated types to work in default impls, as the bounds
191191
/// are visible both as projection bounds and as where-clauses from the
192192
/// parameter environment.
@@ -916,6 +916,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
916916
-> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>>
917917
{
918918
let TraitObligationStack { obligation, .. } = *stack;
919+
let ref obligation = Obligation {
920+
cause: obligation.cause.clone(),
921+
recursion_depth: obligation.recursion_depth,
922+
predicate: self.infcx().resolve_type_vars_if_possible(&obligation.predicate)
923+
};
924+
925+
if obligation.predicate.skip_binder().self_ty().is_ty_var() {
926+
// FIXME(#20297): Self is a type variable (e.g. `_: AsRef<str>`).
927+
//
928+
// This is somewhat problematic, as the current scheme can't really
929+
// handle it turning to be a projection. This does end up as truly
930+
// ambiguous in most cases anyway.
931+
//
932+
// Until this is fixed, take the fast path out - this also improves
933+
// performance by preventing assemble_candidates_from_impls from
934+
// matching every impl for this trait.
935+
return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true });
936+
}
919937

920938
let mut candidates = SelectionCandidateSet {
921939
vec: Vec::new(),
@@ -936,13 +954,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
936954

937955
// For other types, we'll use the builtin rules.
938956
try!(self.assemble_builtin_bound_candidates(ty::BoundCopy,
939-
stack,
957+
obligation,
940958
&mut candidates));
941959
}
942960
Some(bound @ ty::BoundSized) => {
943961
// Sized is never implementable by end-users, it is
944962
// always automatically computed.
945-
try!(self.assemble_builtin_bound_candidates(bound, stack, &mut candidates));
963+
try!(self.assemble_builtin_bound_candidates(bound,
964+
obligation,
965+
&mut candidates));
946966
}
947967

948968
None if self.tcx().lang_items.unsize_trait() ==
@@ -975,29 +995,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
975995
obligation: &TraitObligation<'tcx>,
976996
candidates: &mut SelectionCandidateSet<'tcx>)
977997
{
978-
let poly_trait_predicate =
979-
self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
980-
981-
debug!("assemble_candidates_for_projected_tys({:?},{:?})",
982-
obligation,
983-
poly_trait_predicate);
998+
debug!("assemble_candidates_for_projected_tys({:?})", obligation);
984999

9851000
// FIXME(#20297) -- just examining the self-type is very simplistic
9861001

9871002
// before we go into the whole skolemization thing, just
9881003
// quickly check if the self-type is a projection at all.
989-
let trait_def_id = match poly_trait_predicate.0.trait_ref.self_ty().sty {
1004+
let trait_def_id = match obligation.predicate.0.trait_ref.self_ty().sty {
9901005
ty::TyProjection(ref data) => data.trait_ref.def_id,
9911006
ty::TyInfer(ty::TyVar(_)) => {
992-
// If the self-type is an inference variable, then it MAY wind up
993-
// being a projected type, so induce an ambiguity.
994-
//
995-
// FIXME(#20297) -- being strict about this can cause
996-
// inference failures with BorrowFrom, which is
997-
// unfortunate. Can we do better here?
998-
debug!("assemble_candidates_for_projected_tys: ambiguous self-type");
999-
candidates.ambiguous = true;
1000-
return;
1007+
self.tcx().sess.span_bug(obligation.cause.span,
1008+
"Self=_ should have been handled by assemble_candidates");
10011009
}
10021010
_ => { return; }
10031011
};
@@ -1165,7 +1173,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11651173
// ok to skip binder because the substs on closure types never
11661174
// touch bound regions, they just capture the in-scope
11671175
// type/region parameters
1168-
let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
1176+
let self_ty = *obligation.self_ty().skip_binder();
11691177
let (closure_def_id, substs) = match self_ty.sty {
11701178
ty::TyClosure(id, ref substs) => (id, substs),
11711179
ty::TyInfer(ty::TyVar(_)) => {
@@ -1209,7 +1217,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12091217
}
12101218

12111219
// ok to skip binder because what we are inspecting doesn't involve bound regions
1212-
let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
1220+
let self_ty = *obligation.self_ty().skip_binder();
12131221
match self_ty.sty {
12141222
ty::TyInfer(ty::TyVar(_)) => {
12151223
debug!("assemble_fn_pointer_candidates: ambiguous self-type");
@@ -1266,7 +1274,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12661274
-> Result<(), SelectionError<'tcx>>
12671275
{
12681276
// OK to skip binder here because the tests we do below do not involve bound regions
1269-
let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
1277+
let self_ty = *obligation.self_ty().skip_binder();
12701278
debug!("assemble_candidates_from_default_impls(self_ty={:?})", self_ty);
12711279

12721280
let def_id = obligation.predicate.def_id();
@@ -1322,7 +1330,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13221330
candidates: &mut SelectionCandidateSet<'tcx>)
13231331
{
13241332
debug!("assemble_candidates_from_object_ty(self_ty={:?})",
1325-
self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()));
1333+
obligation.self_ty().skip_binder());
13261334

13271335
// Object-safety candidates are only applicable to object-safe
13281336
// traits. Including this check is useful because it helps
@@ -1337,10 +1345,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13371345
}
13381346

13391347
self.infcx.commit_if_ok(|snapshot| {
1340-
let bound_self_ty =
1341-
self.infcx.resolve_type_vars_if_possible(&obligation.self_ty());
13421348
let (self_ty, _) =
1343-
self.infcx().skolemize_late_bound_regions(&bound_self_ty, snapshot);
1349+
self.infcx().skolemize_late_bound_regions(&obligation.self_ty(), snapshot);
13441350
let poly_trait_ref = match self_ty.sty {
13451351
ty::TyTrait(ref data) => {
13461352
match self.tcx().lang_items.to_builtin_kind(obligation.predicate.def_id()) {
@@ -1414,15 +1420,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14141420
// T: Trait
14151421
// so it seems ok if we (conservatively) fail to accept that `Unsize`
14161422
// obligation above. Should be possible to extend this in the future.
1417-
let self_ty = match self.tcx().no_late_bound_regions(&obligation.self_ty()) {
1423+
let source = match self.tcx().no_late_bound_regions(&obligation.self_ty()) {
14181424
Some(t) => t,
14191425
None => {
14201426
// Don't add any candidates if there are bound regions.
14211427
return;
14221428
}
14231429
};
1424-
let source = self.infcx.shallow_resolve(self_ty);
1425-
let target = self.infcx.shallow_resolve(obligation.predicate.0.input_types()[0]);
1430+
let target = obligation.predicate.0.input_types()[0];
14261431

14271432
debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})",
14281433
source, target);
@@ -1577,11 +1582,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15771582

15781583
fn assemble_builtin_bound_candidates<'o>(&mut self,
15791584
bound: ty::BuiltinBound,
1580-
stack: &TraitObligationStack<'o, 'tcx>,
1585+
obligation: &TraitObligation<'tcx>,
15811586
candidates: &mut SelectionCandidateSet<'tcx>)
15821587
-> Result<(),SelectionError<'tcx>>
15831588
{
1584-
match self.builtin_bound(bound, stack.obligation) {
1589+
match self.builtin_bound(bound, obligation) {
15851590
Ok(If(..)) => {
15861591
debug!("builtin_bound: bound={:?}",
15871592
bound);

0 commit comments

Comments
 (0)