Skip to content

Commit ab86bf5

Browse files
Ariel Ben-Yehudaarielb1
Ariel Ben-Yehuda
authored andcommitted
consolidate type-variable handling in assemble_candidates
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.
1 parent cfd76b3 commit ab86bf5

File tree

1 file changed

+36
-31
lines changed

1 file changed

+36
-31
lines changed

src/librustc/middle/traits/select.rs

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

919937
let mut candidates = SelectionCandidateSet {
920938
vec: Vec::new(),
@@ -935,13 +953,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
935953

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

947967
None if self.tcx().lang_items.unsize_trait() ==
@@ -974,29 +994,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
974994
obligation: &TraitObligation<'tcx>,
975995
candidates: &mut SelectionCandidateSet<'tcx>)
976996
{
977-
let poly_trait_predicate =
978-
self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
979-
980-
debug!("assemble_candidates_for_projected_tys({:?},{:?})",
981-
obligation,
982-
poly_trait_predicate);
997+
debug!("assemble_candidates_for_projected_tys({:?})", obligation);
983998

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

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

12101218
// ok to skip binder because what we are inspecting doesn't involve bound regions
1211-
let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
1219+
let self_ty = *obligation.self_ty().skip_binder();
12121220
match self_ty.sty {
12131221
ty::TyInfer(ty::TyVar(_)) => {
12141222
debug!("assemble_fn_pointer_candidates: ambiguous self-type");
@@ -1265,7 +1273,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12651273
-> Result<(), SelectionError<'tcx>>
12661274
{
12671275
// OK to skip binder here because the tests we do below do not involve bound regions
1268-
let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
1276+
let self_ty = *obligation.self_ty().skip_binder();
12691277
debug!("assemble_candidates_from_default_impls(self_ty={:?})", self_ty);
12701278

12711279
let def_id = obligation.predicate.def_id();
@@ -1321,7 +1329,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13211329
candidates: &mut SelectionCandidateSet<'tcx>)
13221330
{
13231331
debug!("assemble_candidates_from_object_ty(self_ty={:?})",
1324-
self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()));
1332+
obligation.self_ty().skip_binder());
13251333

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

13381346
self.infcx.commit_if_ok(|snapshot| {
1339-
let bound_self_ty =
1340-
self.infcx.resolve_type_vars_if_possible(&obligation.self_ty());
13411347
let (self_ty, _) =
1342-
self.infcx().skolemize_late_bound_regions(&bound_self_ty, snapshot);
1348+
self.infcx().skolemize_late_bound_regions(&obligation.self_ty(), snapshot);
13431349
let poly_trait_ref = match self_ty.sty {
13441350
ty::TyTrait(ref data) => {
13451351
match self.tcx().lang_items.to_builtin_kind(obligation.predicate.def_id()) {
@@ -1413,15 +1419,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14131419
// T: Trait
14141420
// so it seems ok if we (conservatively) fail to accept that `Unsize`
14151421
// obligation above. Should be possible to extend this in the future.
1416-
let self_ty = match self.tcx().no_late_bound_regions(&obligation.self_ty()) {
1422+
let source = match self.tcx().no_late_bound_regions(&obligation.self_ty()) {
14171423
Some(t) => t,
14181424
None => {
14191425
// Don't add any candidates if there are bound regions.
14201426
return;
14211427
}
14221428
};
1423-
let source = self.infcx.shallow_resolve(self_ty);
1424-
let target = self.infcx.shallow_resolve(obligation.predicate.0.input_types()[0]);
1429+
let target = obligation.predicate.0.input_types()[0];
14251430

14261431
debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})",
14271432
source, target);
@@ -1576,11 +1581,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15761581

15771582
fn assemble_builtin_bound_candidates<'o>(&mut self,
15781583
bound: ty::BuiltinBound,
1579-
stack: &TraitObligationStack<'o, 'tcx>,
1584+
obligation: &TraitObligation<'tcx>,
15801585
candidates: &mut SelectionCandidateSet<'tcx>)
15811586
-> Result<(),SelectionError<'tcx>>
15821587
{
1583-
match self.builtin_bound(bound, stack.obligation) {
1588+
match self.builtin_bound(bound, obligation) {
15841589
Ok(If(..)) => {
15851590
debug!("builtin_bound: bound={:?}",
15861591
bound);

0 commit comments

Comments
 (0)