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