Skip to content

Commit a43533a

Browse files
author
Ariel Ben-Yehuda
committed
simplify and reduce the size of EvaluationResult
1 parent 768630b commit a43533a

File tree

6 files changed

+173
-56
lines changed

6 files changed

+173
-56
lines changed

src/librustc/middle/traits/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub use self::object_safety::object_safety_violations;
4444
pub use self::object_safety::ObjectSafetyViolation;
4545
pub use self::object_safety::MethodViolationCode;
4646
pub use self::object_safety::is_vtable_safe_method;
47+
pub use self::select::EvaluationCache;
4748
pub use self::select::SelectionContext;
4849
pub use self::select::SelectionCache;
4950
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};

src/librustc/middle/traits/select.rs

+98-56
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,24 @@ enum BuiltinBoundConditions<'tcx> {
236236
AmbiguousBuiltin
237237
}
238238

239-
#[derive(Debug)]
240-
enum EvaluationResult<'tcx> {
239+
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
240+
/// The result of trait evaluation. The order is important
241+
/// here as the evaluation of a list is the maximum of the
242+
/// evaluations.
243+
enum EvaluationResult {
244+
/// Evaluation successful
241245
EvaluatedToOk,
246+
/// Evaluation failed because of recursion - treated as ambiguous
247+
EvaluatedToUnknown,
248+
/// Evaluation is known to be ambiguous
242249
EvaluatedToAmbig,
243-
EvaluatedToErr(SelectionError<'tcx>),
250+
/// Evaluation failed
251+
EvaluatedToErr,
252+
}
253+
254+
#[derive(Clone)]
255+
pub struct EvaluationCache<'tcx> {
256+
hashmap: RefCell<FnvHashMap<ty::PolyTraitRef<'tcx>, EvaluationResult>>
244257
}
245258

246259
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
@@ -381,6 +394,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
381394
// The result is "true" if the obligation *may* hold and "false" if
382395
// we can be sure it does not.
383396

397+
384398
/// Evaluates whether the obligation `obligation` can be satisfied (by any means).
385399
pub fn evaluate_obligation(&mut self,
386400
obligation: &PredicateObligation<'tcx>)
@@ -393,41 +407,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
393407
.may_apply()
394408
}
395409

396-
fn evaluate_builtin_bound_recursively<'o>(&mut self,
397-
bound: ty::BuiltinBound,
398-
previous_stack: &TraitObligationStack<'o, 'tcx>,
399-
ty: Ty<'tcx>)
400-
-> EvaluationResult<'tcx>
401-
{
402-
let obligation =
403-
util::predicate_for_builtin_bound(
404-
self.tcx(),
405-
previous_stack.obligation.cause.clone(),
406-
bound,
407-
previous_stack.obligation.recursion_depth + 1,
408-
ty);
409-
410-
match obligation {
411-
Ok(obligation) => {
412-
self.evaluate_predicate_recursively(previous_stack.list(), &obligation)
413-
}
414-
Err(ErrorReported) => {
415-
EvaluatedToOk
416-
}
417-
}
418-
}
419-
420410
fn evaluate_predicates_recursively<'a,'o,I>(&mut self,
421411
stack: TraitObligationStackList<'o, 'tcx>,
422412
predicates: I)
423-
-> EvaluationResult<'tcx>
413+
-> EvaluationResult
424414
where I : Iterator<Item=&'a PredicateObligation<'tcx>>, 'tcx:'a
425415
{
426416
let mut result = EvaluatedToOk;
427417
for obligation in predicates {
428418
match self.evaluate_predicate_recursively(stack, obligation) {
429-
EvaluatedToErr(e) => { return EvaluatedToErr(e); }
419+
EvaluatedToErr => { return EvaluatedToErr; }
430420
EvaluatedToAmbig => { result = EvaluatedToAmbig; }
421+
EvaluatedToUnknown => {
422+
if result < EvaluatedToUnknown {
423+
result = EvaluatedToUnknown;
424+
}
425+
}
431426
EvaluatedToOk => { }
432427
}
433428
}
@@ -437,7 +432,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
437432
fn evaluate_predicate_recursively<'o>(&mut self,
438433
previous_stack: TraitObligationStackList<'o, 'tcx>,
439434
obligation: &PredicateObligation<'tcx>)
440-
-> EvaluationResult<'tcx>
435+
-> EvaluationResult
441436
{
442437
debug!("evaluate_predicate_recursively({:?})",
443438
obligation);
@@ -464,7 +459,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
464459
});
465460
match result {
466461
Ok(()) => EvaluatedToOk,
467-
Err(_) => EvaluatedToErr(Unimplemented),
462+
Err(_) => EvaluatedToErr
468463
}
469464
}
470465

@@ -489,7 +484,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
489484
if object_safety::is_object_safe(self.tcx(), trait_def_id) {
490485
EvaluatedToOk
491486
} else {
492-
EvaluatedToErr(Unimplemented)
487+
EvaluatedToErr
493488
}
494489
}
495490

@@ -505,7 +500,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
505500
EvaluatedToAmbig
506501
}
507502
Err(_) => {
508-
EvaluatedToErr(Unimplemented)
503+
EvaluatedToErr
509504
}
510505
}
511506
})
@@ -516,22 +511,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
516511
fn evaluate_obligation_recursively<'o>(&mut self,
517512
previous_stack: TraitObligationStackList<'o, 'tcx>,
518513
obligation: &TraitObligation<'tcx>)
519-
-> EvaluationResult<'tcx>
514+
-> EvaluationResult
520515
{
521516
debug!("evaluate_obligation_recursively({:?})",
522517
obligation);
523518

524519
let stack = self.push_stack(previous_stack, obligation);
520+
let fresh_trait_ref = stack.fresh_trait_ref;
521+
if let Some(result) = self.check_evaluation_cache(fresh_trait_ref) {
522+
debug!("CACHE HIT: EVAL({:?})={:?}",
523+
fresh_trait_ref,
524+
result);
525+
return result;
526+
}
525527

526528
let result = self.evaluate_stack(&stack);
527529

528-
debug!("result: {:?}", result);
530+
debug!("CACHE MISS: EVAL({:?})={:?}",
531+
fresh_trait_ref,
532+
result);
533+
self.insert_evaluation_cache(fresh_trait_ref, result);
534+
529535
result
530536
}
531537

532538
fn evaluate_stack<'o>(&mut self,
533539
stack: &TraitObligationStack<'o, 'tcx>)
534-
-> EvaluationResult<'tcx>
540+
-> EvaluationResult
535541
{
536542
// In intercrate mode, whenever any of the types are unbound,
537543
// there can always be an impl. Even if there are no impls in
@@ -559,16 +565,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
559565
// precise still.
560566
let input_types = stack.fresh_trait_ref.0.input_types();
561567
let unbound_input_types = input_types.iter().any(|ty| ty.is_fresh());
562-
if
563-
unbound_input_types &&
564-
(self.intercrate ||
568+
if unbound_input_types && self.intercrate {
569+
debug!("evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous",
570+
stack.fresh_trait_ref);
571+
return EvaluatedToAmbig;
572+
}
573+
if unbound_input_types &&
565574
stack.iter().skip(1).any(
566575
|prev| self.match_fresh_trait_refs(&stack.fresh_trait_ref,
567-
&prev.fresh_trait_ref)))
576+
&prev.fresh_trait_ref))
568577
{
569-
debug!("evaluate_stack({:?}) --> unbound argument, recursion --> ambiguous",
578+
debug!("evaluate_stack({:?}) --> unbound argument, recursive --> giving up",
570579
stack.fresh_trait_ref);
571-
return EvaluatedToAmbig;
580+
return EvaluatedToUnknown;
572581
}
573582

574583
// If there is any previous entry on the stack that precisely
@@ -603,7 +612,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
603612
match self.candidate_from_obligation(stack) {
604613
Ok(Some(c)) => self.winnow_candidate(stack, &c),
605614
Ok(None) => EvaluatedToAmbig,
606-
Err(e) => EvaluatedToErr(e),
615+
Err(..) => EvaluatedToErr
607616
}
608617
}
609618

@@ -637,6 +646,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
637646
})
638647
}
639648

649+
fn pick_evaluation_cache(&self) -> &EvaluationCache<'tcx> {
650+
&self.param_env().evaluation_cache
651+
}
652+
653+
fn check_evaluation_cache(&self, trait_ref: ty::PolyTraitRef<'tcx>)
654+
-> Option<EvaluationResult>
655+
{
656+
let cache = self.pick_evaluation_cache();
657+
cache.hashmap.borrow().get(&trait_ref).cloned()
658+
}
659+
660+
fn insert_evaluation_cache(&mut self,
661+
trait_ref: ty::PolyTraitRef<'tcx>,
662+
result: EvaluationResult)
663+
{
664+
// Avoid caching results that depend on more than just the trait-ref:
665+
// The stack can create EvaluatedToUnknown, and closure signatures
666+
// being yet uninferred can create "spurious" EvaluatedToAmbig.
667+
if result == EvaluatedToUnknown ||
668+
(result == EvaluatedToAmbig && trait_ref.has_closure_types())
669+
{
670+
return;
671+
}
672+
673+
let cache = self.pick_evaluation_cache();
674+
cache.hashmap.borrow_mut().insert(trait_ref, result);
675+
}
676+
640677
///////////////////////////////////////////////////////////////////////////
641678
// CANDIDATE ASSEMBLY
642679
//
@@ -669,7 +706,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
669706

670707
match self.check_candidate_cache(&cache_fresh_trait_pred) {
671708
Some(c) => {
672-
debug!("CACHE HIT: cache_fresh_trait_pred={:?}, candidate={:?}",
709+
debug!("CACHE HIT: SELECT({:?})={:?}",
673710
cache_fresh_trait_pred,
674711
c);
675712
return c;
@@ -681,7 +718,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
681718
let candidate = self.candidate_from_obligation_no_cache(stack);
682719

683720
if self.should_update_candidate_cache(&cache_fresh_trait_pred, &candidate) {
684-
debug!("CACHE MISS: cache_fresh_trait_pred={:?}, candidate={:?}",
721+
debug!("CACHE MISS: SELECT({:?})={:?}",
685722
cache_fresh_trait_pred, candidate);
686723
self.insert_candidate_cache(cache_fresh_trait_pred, candidate.clone());
687724
}
@@ -1138,15 +1175,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11381175
fn evaluate_where_clause<'o>(&mut self,
11391176
stack: &TraitObligationStack<'o, 'tcx>,
11401177
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
1141-
-> EvaluationResult<'tcx>
1178+
-> EvaluationResult
11421179
{
11431180
self.infcx().probe(move |_| {
11441181
match self.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
11451182
Ok(obligations) => {
11461183
self.evaluate_predicates_recursively(stack.list(), obligations.iter())
11471184
}
11481185
Err(()) => {
1149-
EvaluatedToErr(Unimplemented)
1186+
EvaluatedToErr
11501187
}
11511188
}
11521189
})
@@ -1492,15 +1529,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14921529
fn winnow_candidate<'o>(&mut self,
14931530
stack: &TraitObligationStack<'o, 'tcx>,
14941531
candidate: &SelectionCandidate<'tcx>)
1495-
-> EvaluationResult<'tcx>
1532+
-> EvaluationResult
14961533
{
14971534
debug!("winnow_candidate: candidate={:?}", candidate);
14981535
let result = self.infcx.probe(|_| {
14991536
let candidate = (*candidate).clone();
15001537
match self.confirm_candidate(stack.obligation, candidate) {
15011538
Ok(selection) => self.winnow_selection(stack.list(),
15021539
selection),
1503-
Err(error) => EvaluatedToErr(error),
1540+
Err(..) => EvaluatedToErr
15041541
}
15051542
});
15061543
debug!("winnow_candidate depth={} result={:?}",
@@ -1511,7 +1548,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15111548
fn winnow_selection<'o>(&mut self,
15121549
stack: TraitObligationStackList<'o,'tcx>,
15131550
selection: Selection<'tcx>)
1514-
-> EvaluationResult<'tcx>
1551+
-> EvaluationResult
15151552
{
15161553
self.evaluate_predicates_recursively(stack,
15171554
selection.nested_obligations().iter())
@@ -2956,6 +2993,14 @@ impl<'tcx> SelectionCache<'tcx> {
29562993
}
29572994
}
29582995

2996+
impl<'tcx> EvaluationCache<'tcx> {
2997+
pub fn new() -> EvaluationCache<'tcx> {
2998+
EvaluationCache {
2999+
hashmap: RefCell::new(FnvHashMap())
3000+
}
3001+
}
3002+
}
3003+
29593004
impl<'o,'tcx> TraitObligationStack<'o,'tcx> {
29603005
fn list(&'o self) -> TraitObligationStackList<'o,'tcx> {
29613006
TraitObligationStackList::with(self)
@@ -3001,17 +3046,14 @@ impl<'o,'tcx> fmt::Debug for TraitObligationStack<'o,'tcx> {
30013046
}
30023047
}
30033048

3004-
impl<'tcx> EvaluationResult<'tcx> {
3049+
impl EvaluationResult {
30053050
fn may_apply(&self) -> bool {
30063051
match *self {
30073052
EvaluatedToOk |
30083053
EvaluatedToAmbig |
3009-
EvaluatedToErr(OutputTypeParameterMismatch(..)) |
3010-
EvaluatedToErr(TraitNotObjectSafe(_)) =>
3011-
true,
3054+
EvaluatedToUnknown => true,
30123055

3013-
EvaluatedToErr(Unimplemented) =>
3014-
false,
3056+
EvaluatedToErr => false
30153057
}
30163058
}
30173059
}

src/librustc/middle/ty/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,9 @@ pub struct ParameterEnvironment<'a, 'tcx:'a> {
10911091
/// for things that have to do with the parameters in scope.
10921092
pub selection_cache: traits::SelectionCache<'tcx>,
10931093

1094+
/// Caches the results of trait evaluation.
1095+
pub evaluation_cache: traits::EvaluationCache<'tcx>,
1096+
10941097
/// Scope that is attached to free regions for this scope. This
10951098
/// is usually the id of the fn body, but for more abstract scopes
10961099
/// like structs we often use the node-id of the struct.
@@ -1112,6 +1115,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
11121115
implicit_region_bound: self.implicit_region_bound,
11131116
caller_bounds: caller_bounds,
11141117
selection_cache: traits::SelectionCache::new(),
1118+
evaluation_cache: traits::EvaluationCache::new(),
11151119
free_id: self.free_id,
11161120
}
11171121
}
@@ -2584,6 +2588,7 @@ impl<'tcx> ctxt<'tcx> {
25842588
caller_bounds: Vec::new(),
25852589
implicit_region_bound: ty::ReEmpty,
25862590
selection_cache: traits::SelectionCache::new(),
2591+
evaluation_cache: traits::EvaluationCache::new(),
25872592

25882593
// for an empty parameter
25892594
// environment, there ARE no free
@@ -2673,6 +2678,7 @@ impl<'tcx> ctxt<'tcx> {
26732678
implicit_region_bound: ty::ReScope(free_id_outlive),
26742679
caller_bounds: predicates,
26752680
selection_cache: traits::SelectionCache::new(),
2681+
evaluation_cache: traits::EvaluationCache::new(),
26762682
free_id: free_id,
26772683
};
26782684

src/librustc/middle/ty/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where '
822822
implicit_region_bound: self.implicit_region_bound.fold_with(folder),
823823
caller_bounds: self.caller_bounds.fold_with(folder),
824824
selection_cache: traits::SelectionCache::new(),
825+
evaluation_cache: traits::EvaluationCache::new(),
825826
free_id: self.free_id,
826827
}
827828
}

0 commit comments

Comments
 (0)