@@ -236,11 +236,24 @@ enum BuiltinBoundConditions<'tcx> {
236
236
AmbiguousBuiltin
237
237
}
238
238
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
241
245
EvaluatedToOk ,
246
+ /// Evaluation failed because of recursion - treated as ambiguous
247
+ EvaluatedToUnknown ,
248
+ /// Evaluation is known to be ambiguous
242
249
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 > >
244
257
}
245
258
246
259
impl < ' cx , ' tcx > SelectionContext < ' cx , ' tcx > {
@@ -381,6 +394,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
381
394
// The result is "true" if the obligation *may* hold and "false" if
382
395
// we can be sure it does not.
383
396
397
+
384
398
/// Evaluates whether the obligation `obligation` can be satisfied (by any means).
385
399
pub fn evaluate_obligation ( & mut self ,
386
400
obligation : & PredicateObligation < ' tcx > )
@@ -393,41 +407,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
393
407
. may_apply ( )
394
408
}
395
409
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
-
420
410
fn evaluate_predicates_recursively < ' a , ' o , I > ( & mut self ,
421
411
stack : TraitObligationStackList < ' o , ' tcx > ,
422
412
predicates : I )
423
- -> EvaluationResult < ' tcx >
413
+ -> EvaluationResult
424
414
where I : Iterator < Item =& ' a PredicateObligation < ' tcx > > , ' tcx : ' a
425
415
{
426
416
let mut result = EvaluatedToOk ;
427
417
for obligation in predicates {
428
418
match self . evaluate_predicate_recursively ( stack, obligation) {
429
- EvaluatedToErr ( e ) => { return EvaluatedToErr ( e ) ; }
419
+ EvaluatedToErr => { return EvaluatedToErr ; }
430
420
EvaluatedToAmbig => { result = EvaluatedToAmbig ; }
421
+ EvaluatedToUnknown => {
422
+ if result < EvaluatedToUnknown {
423
+ result = EvaluatedToUnknown ;
424
+ }
425
+ }
431
426
EvaluatedToOk => { }
432
427
}
433
428
}
@@ -437,7 +432,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
437
432
fn evaluate_predicate_recursively < ' o > ( & mut self ,
438
433
previous_stack : TraitObligationStackList < ' o , ' tcx > ,
439
434
obligation : & PredicateObligation < ' tcx > )
440
- -> EvaluationResult < ' tcx >
435
+ -> EvaluationResult
441
436
{
442
437
debug ! ( "evaluate_predicate_recursively({:?})" ,
443
438
obligation) ;
@@ -464,7 +459,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
464
459
} ) ;
465
460
match result {
466
461
Ok ( ( ) ) => EvaluatedToOk ,
467
- Err ( _) => EvaluatedToErr ( Unimplemented ) ,
462
+ Err ( _) => EvaluatedToErr
468
463
}
469
464
}
470
465
@@ -489,7 +484,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
489
484
if object_safety:: is_object_safe ( self . tcx ( ) , trait_def_id) {
490
485
EvaluatedToOk
491
486
} else {
492
- EvaluatedToErr ( Unimplemented )
487
+ EvaluatedToErr
493
488
}
494
489
}
495
490
@@ -505,7 +500,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
505
500
EvaluatedToAmbig
506
501
}
507
502
Err ( _) => {
508
- EvaluatedToErr ( Unimplemented )
503
+ EvaluatedToErr
509
504
}
510
505
}
511
506
} )
@@ -516,22 +511,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
516
511
fn evaluate_obligation_recursively < ' o > ( & mut self ,
517
512
previous_stack : TraitObligationStackList < ' o , ' tcx > ,
518
513
obligation : & TraitObligation < ' tcx > )
519
- -> EvaluationResult < ' tcx >
514
+ -> EvaluationResult
520
515
{
521
516
debug ! ( "evaluate_obligation_recursively({:?})" ,
522
517
obligation) ;
523
518
524
519
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
+ }
525
527
526
528
let result = self . evaluate_stack ( & stack) ;
527
529
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
+
529
535
result
530
536
}
531
537
532
538
fn evaluate_stack < ' o > ( & mut self ,
533
539
stack : & TraitObligationStack < ' o , ' tcx > )
534
- -> EvaluationResult < ' tcx >
540
+ -> EvaluationResult
535
541
{
536
542
// In intercrate mode, whenever any of the types are unbound,
537
543
// there can always be an impl. Even if there are no impls in
@@ -559,16 +565,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
559
565
// precise still.
560
566
let input_types = stack. fresh_trait_ref . 0 . input_types ( ) ;
561
567
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 &&
565
574
stack. iter ( ) . skip ( 1 ) . any (
566
575
|prev| self . match_fresh_trait_refs ( & stack. fresh_trait_ref ,
567
- & prev. fresh_trait_ref ) ) )
576
+ & prev. fresh_trait_ref ) )
568
577
{
569
- debug ! ( "evaluate_stack({:?}) --> unbound argument, recursion --> ambiguous " ,
578
+ debug ! ( "evaluate_stack({:?}) --> unbound argument, recursive --> giving up " ,
570
579
stack. fresh_trait_ref) ;
571
- return EvaluatedToAmbig ;
580
+ return EvaluatedToUnknown ;
572
581
}
573
582
574
583
// If there is any previous entry on the stack that precisely
@@ -603,7 +612,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
603
612
match self . candidate_from_obligation ( stack) {
604
613
Ok ( Some ( c) ) => self . winnow_candidate ( stack, & c) ,
605
614
Ok ( None ) => EvaluatedToAmbig ,
606
- Err ( e ) => EvaluatedToErr ( e ) ,
615
+ Err ( .. ) => EvaluatedToErr
607
616
}
608
617
}
609
618
@@ -637,6 +646,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
637
646
} )
638
647
}
639
648
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
+
640
677
///////////////////////////////////////////////////////////////////////////
641
678
// CANDIDATE ASSEMBLY
642
679
//
@@ -669,7 +706,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
669
706
670
707
match self . check_candidate_cache ( & cache_fresh_trait_pred) {
671
708
Some ( c) => {
672
- debug ! ( "CACHE HIT: cache_fresh_trait_pred= {:?}, candidate ={:?}" ,
709
+ debug ! ( "CACHE HIT: SELECT( {:?}) ={:?}" ,
673
710
cache_fresh_trait_pred,
674
711
c) ;
675
712
return c;
@@ -681,7 +718,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
681
718
let candidate = self . candidate_from_obligation_no_cache ( stack) ;
682
719
683
720
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( {:?}) ={:?}" ,
685
722
cache_fresh_trait_pred, candidate) ;
686
723
self . insert_candidate_cache ( cache_fresh_trait_pred, candidate. clone ( ) ) ;
687
724
}
@@ -1138,15 +1175,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
1138
1175
fn evaluate_where_clause < ' o > ( & mut self ,
1139
1176
stack : & TraitObligationStack < ' o , ' tcx > ,
1140
1177
where_clause_trait_ref : ty:: PolyTraitRef < ' tcx > )
1141
- -> EvaluationResult < ' tcx >
1178
+ -> EvaluationResult
1142
1179
{
1143
1180
self . infcx ( ) . probe ( move |_| {
1144
1181
match self . match_where_clause_trait_ref ( stack. obligation , where_clause_trait_ref) {
1145
1182
Ok ( obligations) => {
1146
1183
self . evaluate_predicates_recursively ( stack. list ( ) , obligations. iter ( ) )
1147
1184
}
1148
1185
Err ( ( ) ) => {
1149
- EvaluatedToErr ( Unimplemented )
1186
+ EvaluatedToErr
1150
1187
}
1151
1188
}
1152
1189
} )
@@ -1492,15 +1529,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
1492
1529
fn winnow_candidate < ' o > ( & mut self ,
1493
1530
stack : & TraitObligationStack < ' o , ' tcx > ,
1494
1531
candidate : & SelectionCandidate < ' tcx > )
1495
- -> EvaluationResult < ' tcx >
1532
+ -> EvaluationResult
1496
1533
{
1497
1534
debug ! ( "winnow_candidate: candidate={:?}" , candidate) ;
1498
1535
let result = self . infcx . probe ( |_| {
1499
1536
let candidate = ( * candidate) . clone ( ) ;
1500
1537
match self . confirm_candidate ( stack. obligation , candidate) {
1501
1538
Ok ( selection) => self . winnow_selection ( stack. list ( ) ,
1502
1539
selection) ,
1503
- Err ( error ) => EvaluatedToErr ( error ) ,
1540
+ Err ( .. ) => EvaluatedToErr
1504
1541
}
1505
1542
} ) ;
1506
1543
debug ! ( "winnow_candidate depth={} result={:?}" ,
@@ -1511,7 +1548,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
1511
1548
fn winnow_selection < ' o > ( & mut self ,
1512
1549
stack : TraitObligationStackList < ' o , ' tcx > ,
1513
1550
selection : Selection < ' tcx > )
1514
- -> EvaluationResult < ' tcx >
1551
+ -> EvaluationResult
1515
1552
{
1516
1553
self . evaluate_predicates_recursively ( stack,
1517
1554
selection. nested_obligations ( ) . iter ( ) )
@@ -2956,6 +2993,14 @@ impl<'tcx> SelectionCache<'tcx> {
2956
2993
}
2957
2994
}
2958
2995
2996
+ impl < ' tcx > EvaluationCache < ' tcx > {
2997
+ pub fn new ( ) -> EvaluationCache < ' tcx > {
2998
+ EvaluationCache {
2999
+ hashmap : RefCell :: new ( FnvHashMap ( ) )
3000
+ }
3001
+ }
3002
+ }
3003
+
2959
3004
impl < ' o , ' tcx > TraitObligationStack < ' o , ' tcx > {
2960
3005
fn list ( & ' o self ) -> TraitObligationStackList < ' o , ' tcx > {
2961
3006
TraitObligationStackList :: with ( self )
@@ -3001,17 +3046,14 @@ impl<'o,'tcx> fmt::Debug for TraitObligationStack<'o,'tcx> {
3001
3046
}
3002
3047
}
3003
3048
3004
- impl < ' tcx > EvaluationResult < ' tcx > {
3049
+ impl EvaluationResult {
3005
3050
fn may_apply ( & self ) -> bool {
3006
3051
match * self {
3007
3052
EvaluatedToOk |
3008
3053
EvaluatedToAmbig |
3009
- EvaluatedToErr ( OutputTypeParameterMismatch ( ..) ) |
3010
- EvaluatedToErr ( TraitNotObjectSafe ( _) ) =>
3011
- true ,
3054
+ EvaluatedToUnknown => true ,
3012
3055
3013
- EvaluatedToErr ( Unimplemented ) =>
3014
- false ,
3056
+ EvaluatedToErr => false
3015
3057
}
3016
3058
}
3017
3059
}
0 commit comments