]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
Separate trait selection from ambiguity reporting.
[rust.git] / compiler / rustc_trait_selection / src / traits / chalk_fulfill.rs
1 //! Defines a Chalk-based `TraitEngine`
2
3 use crate::infer::canonical::OriginalQueryValues;
4 use crate::infer::InferCtxt;
5 use crate::traits::query::NoSolution;
6 use crate::traits::{
7     ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
8     SelectionError, TraitEngine,
9 };
10 use rustc_data_structures::fx::FxIndexSet;
11 use rustc_middle::ty::TypeVisitable;
12
13 pub struct FulfillmentContext<'tcx> {
14     obligations: FxIndexSet<PredicateObligation<'tcx>>,
15
16     usable_in_snapshot: bool,
17 }
18
19 impl FulfillmentContext<'_> {
20     pub(super) fn new() -> Self {
21         FulfillmentContext { obligations: FxIndexSet::default(), usable_in_snapshot: false }
22     }
23
24     pub(crate) fn new_in_snapshot() -> Self {
25         FulfillmentContext { usable_in_snapshot: true, ..Self::new() }
26     }
27 }
28
29 impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
30     fn register_predicate_obligation(
31         &mut self,
32         infcx: &InferCtxt<'tcx>,
33         obligation: PredicateObligation<'tcx>,
34     ) {
35         if !self.usable_in_snapshot {
36             assert!(!infcx.is_in_snapshot());
37         }
38         let obligation = infcx.resolve_vars_if_possible(obligation);
39
40         self.obligations.insert(obligation);
41     }
42
43     fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> {
44         // any remaining obligations are errors
45         self.obligations
46             .iter()
47             .map(|obligation| FulfillmentError {
48                 obligation: obligation.clone(),
49                 code: FulfillmentErrorCode::CodeAmbiguity,
50                 // FIXME - does Chalk have a notation of 'root obligation'?
51                 // This is just for diagnostics, so it's okay if this is wrong
52                 root_obligation: obligation.clone(),
53             })
54             .collect()
55     }
56
57     fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
58         if !self.usable_in_snapshot {
59             assert!(!infcx.is_in_snapshot());
60         }
61
62         let mut errors = Vec::new();
63         let mut next_round = FxIndexSet::default();
64         let mut making_progress;
65
66         loop {
67             making_progress = false;
68
69             // We iterate over all obligations, and record if we are able
70             // to unambiguously prove at least one obligation.
71             for obligation in self.obligations.drain(..) {
72                 let obligation = infcx.resolve_vars_if_possible(obligation);
73                 let environment = obligation.param_env.caller_bounds();
74                 let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
75                 let mut orig_values = OriginalQueryValues::default();
76                 if goal.references_error() {
77                     continue;
78                 }
79
80                 let canonical_goal =
81                     infcx.canonicalize_query_preserving_universes(goal, &mut orig_values);
82
83                 match infcx.tcx.evaluate_goal(canonical_goal) {
84                     Ok(response) => {
85                         if response.is_proven() {
86                             making_progress = true;
87
88                             match infcx.instantiate_query_response_and_region_obligations(
89                                 &obligation.cause,
90                                 obligation.param_env,
91                                 &orig_values,
92                                 &response,
93                             ) {
94                                 Ok(infer_ok) => next_round.extend(
95                                     infer_ok.obligations.into_iter().map(|obligation| {
96                                         assert!(!infcx.is_in_snapshot());
97                                         infcx.resolve_vars_if_possible(obligation)
98                                     }),
99                                 ),
100
101                                 Err(_err) => errors.push(FulfillmentError {
102                                     obligation: obligation.clone(),
103                                     code: FulfillmentErrorCode::CodeSelectionError(
104                                         SelectionError::Unimplemented,
105                                     ),
106                                     // FIXME - does Chalk have a notation of 'root obligation'?
107                                     // This is just for diagnostics, so it's okay if this is wrong
108                                     root_obligation: obligation,
109                                 }),
110                             }
111                         } else {
112                             // Ambiguous: retry at next round.
113                             next_round.insert(obligation);
114                         }
115                     }
116
117                     Err(NoSolution) => errors.push(FulfillmentError {
118                         obligation: obligation.clone(),
119                         code: FulfillmentErrorCode::CodeSelectionError(
120                             SelectionError::Unimplemented,
121                         ),
122                         // FIXME - does Chalk have a notation of 'root obligation'?
123                         // This is just for diagnostics, so it's okay if this is wrong
124                         root_obligation: obligation,
125                     }),
126                 }
127             }
128             next_round = std::mem::replace(&mut self.obligations, next_round);
129
130             if !making_progress {
131                 break;
132             }
133         }
134
135         errors
136     }
137
138     fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
139         self.obligations.iter().cloned().collect()
140     }
141 }