]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
Auto merge of #88086 - ssomers:btree_clone_testing, r=dtolnay
[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, ObligationCause,
8     PredicateObligation, SelectionError, TraitEngine,
9 };
10 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
11 use rustc_middle::ty::{self, Ty};
12
13 pub struct FulfillmentContext<'tcx> {
14     obligations: FxIndexSet<PredicateObligation<'tcx>>,
15
16     relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
17 }
18
19 impl FulfillmentContext<'tcx> {
20     crate fn new() -> Self {
21         FulfillmentContext {
22             obligations: FxIndexSet::default(),
23             relationships: FxHashMap::default(),
24         }
25     }
26 }
27
28 impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
29     fn normalize_projection_type(
30         &mut self,
31         infcx: &InferCtxt<'_, 'tcx>,
32         _param_env: ty::ParamEnv<'tcx>,
33         projection_ty: ty::ProjectionTy<'tcx>,
34         _cause: ObligationCause<'tcx>,
35     ) -> Ty<'tcx> {
36         infcx.tcx.mk_ty(ty::Projection(projection_ty))
37     }
38
39     fn register_predicate_obligation(
40         &mut self,
41         infcx: &InferCtxt<'_, 'tcx>,
42         obligation: PredicateObligation<'tcx>,
43     ) {
44         assert!(!infcx.is_in_snapshot());
45         let obligation = infcx.resolve_vars_if_possible(obligation);
46
47         super::relationships::update(self, infcx, &obligation);
48
49         self.obligations.insert(obligation);
50     }
51
52     fn select_all_or_error(
53         &mut self,
54         infcx: &InferCtxt<'_, 'tcx>,
55     ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
56         self.select_where_possible(infcx)?;
57
58         if self.obligations.is_empty() {
59             Ok(())
60         } else {
61             let errors = self
62                 .obligations
63                 .iter()
64                 .map(|obligation| FulfillmentError {
65                     obligation: obligation.clone(),
66                     code: FulfillmentErrorCode::CodeAmbiguity,
67                     // FIXME - does Chalk have a notation of 'root obligation'?
68                     // This is just for diagnostics, so it's okay if this is wrong
69                     root_obligation: obligation.clone(),
70                 })
71                 .collect();
72             Err(errors)
73         }
74     }
75
76     fn select_where_possible(
77         &mut self,
78         infcx: &InferCtxt<'_, 'tcx>,
79     ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
80         assert!(!infcx.is_in_snapshot());
81
82         let mut errors = Vec::new();
83         let mut next_round = FxIndexSet::default();
84         let mut making_progress;
85
86         loop {
87             making_progress = false;
88
89             // We iterate over all obligations, and record if we are able
90             // to unambiguously prove at least one obligation.
91             for obligation in self.obligations.drain(..) {
92                 let obligation = infcx.resolve_vars_if_possible(obligation);
93                 let environment = obligation.param_env.caller_bounds();
94                 let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
95                 let mut orig_values = OriginalQueryValues::default();
96                 let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
97
98                 match infcx.tcx.evaluate_goal(canonical_goal) {
99                     Ok(response) => {
100                         if response.is_proven() {
101                             making_progress = true;
102
103                             match infcx.instantiate_query_response_and_region_obligations(
104                                 &obligation.cause,
105                                 obligation.param_env,
106                                 &orig_values,
107                                 &response,
108                             ) {
109                                 Ok(infer_ok) => next_round.extend(
110                                     infer_ok.obligations.into_iter().map(|obligation| {
111                                         assert!(!infcx.is_in_snapshot());
112                                         infcx.resolve_vars_if_possible(obligation)
113                                     }),
114                                 ),
115
116                                 Err(_err) => errors.push(FulfillmentError {
117                                     obligation: obligation.clone(),
118                                     code: FulfillmentErrorCode::CodeSelectionError(
119                                         SelectionError::Unimplemented,
120                                     ),
121                                     // FIXME - does Chalk have a notation of 'root obligation'?
122                                     // This is just for diagnostics, so it's okay if this is wrong
123                                     root_obligation: obligation,
124                                 }),
125                             }
126                         } else {
127                             // Ambiguous: retry at next round.
128                             next_round.insert(obligation);
129                         }
130                     }
131
132                     Err(NoSolution) => errors.push(FulfillmentError {
133                         obligation: obligation.clone(),
134                         code: FulfillmentErrorCode::CodeSelectionError(
135                             SelectionError::Unimplemented,
136                         ),
137                         // FIXME - does Chalk have a notation of 'root obligation'?
138                         // This is just for diagnostics, so it's okay if this is wrong
139                         root_obligation: obligation,
140                     }),
141                 }
142             }
143             next_round = std::mem::replace(&mut self.obligations, next_round);
144
145             if !making_progress {
146                 break;
147             }
148         }
149
150         if errors.is_empty() { Ok(()) } else { Err(errors) }
151     }
152
153     fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
154         self.obligations.iter().cloned().collect()
155     }
156
157     fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
158         &mut self.relationships
159     }
160 }