Skip to content

Commit 984dc03

Browse files
committed
Do not cache ambiguous results unless there is at least some inference by-product within.
Fixes #19499.
1 parent 8160fc4 commit 984dc03

File tree

2 files changed

+77
-4
lines changed

2 files changed

+77
-4
lines changed

src/librustc/middle/traits/select.rs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -526,9 +526,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
526526

527527
// If no match, compute result and insert into cache.
528528
let candidate = self.candidate_from_obligation_no_cache(stack);
529-
debug!("CACHE MISS: cache_fresh_trait_pred={}, candidate={}",
530-
cache_fresh_trait_pred.repr(self.tcx()), candidate.repr(self.tcx()));
531-
self.insert_candidate_cache(cache_fresh_trait_pred, candidate.clone());
529+
530+
if self.should_update_candidate_cache(&cache_fresh_trait_pred, &candidate) {
531+
debug!("CACHE MISS: cache_fresh_trait_pred={}, candidate={}",
532+
cache_fresh_trait_pred.repr(self.tcx()), candidate.repr(self.tcx()));
533+
self.insert_candidate_cache(cache_fresh_trait_pred, candidate.clone());
534+
}
535+
532536
candidate
533537
}
534538

@@ -705,6 +709,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
705709
hashmap.insert(cache_fresh_trait_pred.0.trait_ref.clone(), candidate);
706710
}
707711

712+
fn should_update_candidate_cache(&mut self,
713+
cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>,
714+
candidate: &SelectionResult<'tcx, SelectionCandidate<'tcx>>)
715+
-> bool
716+
{
717+
// In general, it's a good idea to cache results, even
718+
// ambigious ones, to save us some trouble later. But we have
719+
// to be careful not to cache results that could be
720+
// invalidated later by advances in inference. Normally, this
721+
// is not an issue, because any inference variables whose
722+
// types are not yet bound are "freshened" in the cache key,
723+
// which means that if we later get the same request once that
724+
// type variable IS bound, we'll have a different cache key.
725+
// For example, if we have `Vec<_#0t> : Foo`, and `_#0t` is
726+
// not yet known, we may cache the result as `None`. But if
727+
// later `_#0t` is bound to `Bar`, then when we freshen we'll
728+
// have `Vec<Bar> : Foo` as the cache key.
729+
//
730+
// HOWEVER, it CAN happen that we get an ambiguity result in
731+
// one particular case around closures where the cache key
732+
// would not change. That is when the precise types of the
733+
// upvars that a closure references have not yet been figured
734+
// out (i.e., because it is not yet known if they are captured
735+
// by ref, and if by ref, what kind of ref). In these cases,
736+
// when matching a builtin bound, we will yield back an
737+
// ambiguous result. But the *cache key* is just the closure type,
738+
// it doesn't capture the state of the upvar computation.
739+
//
740+
// To avoid this trap, just don't cache ambiguous results if
741+
// the self-type contains no inference byproducts (that really
742+
// shouldn't happen in other circumstances anyway, given
743+
// coherence).
744+
745+
match *candidate {
746+
Ok(Some(_)) | Err(_) => true,
747+
Ok(None) => {
748+
cache_fresh_trait_pred.0.input_types().iter().any(|&t| ty::type_has_ty_infer(t))
749+
}
750+
}
751+
}
752+
708753
fn assemble_candidates<'o>(&mut self,
709754
stack: &TraitObligationStack<'o, 'tcx>)
710755
-> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>>
@@ -788,6 +833,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
788833
// FIXME(#20297) -- being strict about this can cause
789834
// inference failures with BorrowFrom, which is
790835
// unfortunate. Can we do better here?
836+
debug!("assemble_candidates_for_projected_tys: ambiguous self-type");
791837
candidates.ambiguous = true;
792838
return;
793839
}
@@ -962,6 +1008,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
9621008
let (closure_def_id, substs) = match self_ty.sty {
9631009
ty::ty_unboxed_closure(id, _, ref substs) => (id, substs.clone()),
9641010
ty::ty_infer(ty::TyVar(_)) => {
1011+
debug!("assemble_unboxed_closure_candidates: ambiguous self-type");
9651012
candidates.ambiguous = true;
9661013
return Ok(());
9671014
}
@@ -1000,6 +1047,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10001047
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
10011048
match self_ty.sty {
10021049
ty::ty_infer(ty::TyVar(_)) => {
1050+
debug!("assemble_fn_pointer_candidates: ambiguous self-type");
10031051
candidates.ambiguous = true; // could wind up being a fn() type
10041052
}
10051053

@@ -1270,7 +1318,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12701318
Ok(())
12711319
}
12721320
Ok(ParameterBuiltin) => { Ok(()) }
1273-
Ok(AmbiguousBuiltin) => { Ok(candidates.ambiguous = true) }
1321+
Ok(AmbiguousBuiltin) => {
1322+
debug!("assemble_builtin_bound_candidates: ambiguous builtin");
1323+
Ok(candidates.ambiguous = true)
1324+
}
12741325
Err(e) => { Err(e) }
12751326
}
12761327
}
@@ -1476,6 +1527,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14761527
Ok(If(upvars.iter().map(|c| c.ty).collect()))
14771528
}
14781529
None => {
1530+
debug!("assemble_builtin_bound_candidates: no upvar types available yet");
14791531
Ok(AmbiguousBuiltin)
14801532
}
14811533
}
@@ -1512,6 +1564,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15121564
// Unbound type variable. Might or might not have
15131565
// applicable impls and so forth, depending on what
15141566
// those type variables wind up being bound to.
1567+
debug!("assemble_builtin_bound_candidates: ambiguous builtin");
15151568
Ok(AmbiguousBuiltin)
15161569
}
15171570

src/test/run-pass/issue-19499.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Regression test for issue #19499. Due to incorrect caching of trait
12+
// results for closures with upvars whose types were not fully
13+
// computed, this rather bizarre little program (along with many more
14+
// reasonable examples) let to ambiguity errors about not being able
15+
// to infer sufficient type information.
16+
17+
fn main() {
18+
let n = 0;
19+
let it = Some(1_us).into_iter().inspect(|_| {n;});
20+
}

0 commit comments

Comments
 (0)