]> git.proxmox.com Git - rustc.git/blob - src/librustc/traits/chalk_fulfill.rs
New upstream version 1.34.2+dfsg1
[rustc.git] / src / librustc / traits / chalk_fulfill.rs
1 use crate::traits::{
2 Environment,
3 InEnvironment,
4 TraitEngine,
5 ObligationCause,
6 PredicateObligation,
7 FulfillmentError,
8 FulfillmentErrorCode,
9 SelectionError,
10 };
11 use crate::traits::query::NoSolution;
12 use crate::infer::InferCtxt;
13 use crate::infer::canonical::{Canonical, OriginalQueryValues};
14 use crate::ty::{self, Ty};
15 use rustc_data_structures::fx::FxHashSet;
16
17 pub type CanonicalGoal<'tcx> = Canonical<'tcx, InEnvironment<'tcx, ty::Predicate<'tcx>>>;
18
19 pub struct FulfillmentContext<'tcx> {
20 obligations: FxHashSet<InEnvironment<'tcx, PredicateObligation<'tcx>>>,
21 }
22
23 impl FulfillmentContext<'tcx> {
24 crate fn new() -> Self {
25 FulfillmentContext {
26 obligations: FxHashSet::default(),
27 }
28 }
29 }
30
31 fn in_environment(
32 infcx: &InferCtxt<'_, 'gcx, 'tcx>,
33 obligation: PredicateObligation<'tcx>
34 ) -> InEnvironment<'tcx, PredicateObligation<'tcx>> {
35 assert!(!infcx.is_in_snapshot());
36 let obligation = infcx.resolve_type_vars_if_possible(&obligation);
37
38 let environment = match obligation.param_env.def_id {
39 Some(def_id) => infcx.tcx.environment(def_id),
40 None if obligation.param_env.caller_bounds.is_empty() => Environment {
41 clauses: ty::List::empty(),
42 },
43 _ => bug!("non-empty `ParamEnv` with no def-id"),
44 };
45
46 InEnvironment {
47 environment,
48 goal: obligation,
49 }
50 }
51
52 impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
53 fn normalize_projection_type(
54 &mut self,
55 infcx: &InferCtxt<'_, 'gcx, 'tcx>,
56 _param_env: ty::ParamEnv<'tcx>,
57 projection_ty: ty::ProjectionTy<'tcx>,
58 _cause: ObligationCause<'tcx>,
59 ) -> Ty<'tcx> {
60 infcx.tcx.mk_ty(ty::Projection(projection_ty))
61 }
62
63 fn register_predicate_obligation(
64 &mut self,
65 infcx: &InferCtxt<'_, 'gcx, 'tcx>,
66 obligation: PredicateObligation<'tcx>,
67 ) {
68 self.obligations.insert(in_environment(infcx, obligation));
69 }
70
71 fn select_all_or_error(
72 &mut self,
73 infcx: &InferCtxt<'_, 'gcx, 'tcx>,
74 ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
75 self.select_where_possible(infcx)?;
76
77 if self.obligations.is_empty() {
78 Ok(())
79 } else {
80 let errors = self.obligations.iter()
81 .map(|obligation| FulfillmentError {
82 obligation: obligation.goal.clone(),
83 code: FulfillmentErrorCode::CodeAmbiguity,
84 })
85 .collect();
86 Err(errors)
87 }
88 }
89
90 fn select_where_possible(
91 &mut self,
92 infcx: &InferCtxt<'_, 'gcx, 'tcx>,
93 ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
94 let mut errors = Vec::new();
95 let mut next_round = FxHashSet::default();
96 let mut making_progress;
97
98 loop {
99 making_progress = false;
100
101 // We iterate over all obligations, and record if we are able
102 // to unambiguously prove at least one obligation.
103 for obligation in self.obligations.drain() {
104 let mut orig_values = OriginalQueryValues::default();
105 let canonical_goal = infcx.canonicalize_query(&InEnvironment {
106 environment: obligation.environment,
107 goal: obligation.goal.predicate,
108 }, &mut orig_values);
109
110 match infcx.tcx.global_tcx().evaluate_goal(canonical_goal) {
111 Ok(response) => {
112 if response.is_proven() {
113 making_progress = true;
114
115 match infcx.instantiate_query_response_and_region_obligations(
116 &obligation.goal.cause,
117 obligation.goal.param_env,
118 &orig_values,
119 &response
120 ) {
121 Ok(infer_ok) => next_round.extend(
122 infer_ok.obligations
123 .into_iter()
124 .map(|obligation| in_environment(infcx, obligation))
125 ),
126
127 Err(_err) => errors.push(FulfillmentError {
128 obligation: obligation.goal,
129 code: FulfillmentErrorCode::CodeSelectionError(
130 SelectionError::Unimplemented
131 ),
132 }),
133 }
134 } else {
135 // Ambiguous: retry at next round.
136 next_round.insert(obligation);
137 }
138 }
139
140 Err(NoSolution) => errors.push(FulfillmentError {
141 obligation: obligation.goal,
142 code: FulfillmentErrorCode::CodeSelectionError(
143 SelectionError::Unimplemented
144 ),
145 })
146 }
147 }
148 next_round = std::mem::replace(&mut self.obligations, next_round);
149
150 if !making_progress {
151 break;
152 }
153 }
154
155 if errors.is_empty() {
156 Ok(())
157 } else {
158 Err(errors)
159 }
160 }
161
162 fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
163 self.obligations.iter().map(|obligation| obligation.goal.clone()).collect()
164 }
165 }