|
11 | 11 |
|
12 | 12 | use std::iter; |
13 | 13 |
|
| 14 | +use canonicalizer::Canonicalizer; |
14 | 15 | use rustc_index::IndexVec; |
15 | | -use rustc_type_ir::data_structures::HashSet; |
16 | 16 | use rustc_type_ir::inherent::*; |
17 | 17 | use rustc_type_ir::relate::solver_relating::RelateExt; |
18 | | -use rustc_type_ir::solve::OpaqueTypesJank; |
19 | 18 | use rustc_type_ir::{ |
20 | 19 | self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, |
21 | 20 | TypeFoldable, |
22 | 21 | }; |
23 | | -use tracing::{debug, instrument, trace}; |
| 22 | +use tracing::instrument; |
24 | 23 |
|
25 | | -use crate::canonicalizer::Canonicalizer; |
26 | 24 | use crate::delegate::SolverDelegate; |
27 | 25 | use crate::resolve::eager_resolve_vars; |
28 | | -use crate::solve::eval_ctxt::CurrentGoalKind; |
29 | 26 | use crate::solve::{ |
30 | 27 | CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, |
31 | | - MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, |
32 | | - QueryResult, Response, inspect, response_no_constraints_raw, |
| 28 | + NestedNormalizationGoals, PredefinedOpaquesData, QueryInput, Response, inspect, |
33 | 29 | }; |
34 | 30 |
|
| 31 | +pub mod canonicalizer; |
| 32 | + |
35 | 33 | trait ResponseT<I: Interner> { |
36 | 34 | fn var_values(&self) -> CanonicalVarValues<I>; |
37 | 35 | } |
@@ -78,199 +76,6 @@ where |
78 | 76 | (orig_values, query_input) |
79 | 77 | } |
80 | 78 |
|
81 | | - /// To return the constraints of a canonical query to the caller, we canonicalize: |
82 | | - /// |
83 | | - /// - `var_values`: a map from bound variables in the canonical goal to |
84 | | - /// the values inferred while solving the instantiated goal. |
85 | | - /// - `external_constraints`: additional constraints which aren't expressible |
86 | | - /// using simple unification of inference variables. |
87 | | - /// |
88 | | - /// This takes the `shallow_certainty` which represents whether we're confident |
89 | | - /// that the final result of the current goal only depends on the nested goals. |
90 | | - /// |
91 | | - /// In case this is `Certainty::Maybe`, there may still be additional nested goals |
92 | | - /// or inference constraints required for this candidate to be hold. The candidate |
93 | | - /// always requires all already added constraints and nested goals. |
94 | | - #[instrument(level = "trace", skip(self), ret)] |
95 | | - pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( |
96 | | - &mut self, |
97 | | - shallow_certainty: Certainty, |
98 | | - ) -> QueryResult<I> { |
99 | | - self.inspect.make_canonical_response(shallow_certainty); |
100 | | - |
101 | | - let goals_certainty = self.try_evaluate_added_goals()?; |
102 | | - assert_eq!( |
103 | | - self.tainted, |
104 | | - Ok(()), |
105 | | - "EvalCtxt is tainted -- nested goals may have been dropped in a \ |
106 | | - previous call to `try_evaluate_added_goals!`" |
107 | | - ); |
108 | | - |
109 | | - // We only check for leaks from universes which were entered inside |
110 | | - // of the query. |
111 | | - self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { |
112 | | - trace!("failed the leak check"); |
113 | | - NoSolution |
114 | | - })?; |
115 | | - |
116 | | - let (certainty, normalization_nested_goals) = |
117 | | - match (self.current_goal_kind, shallow_certainty) { |
118 | | - // When normalizing, we've replaced the expected term with an unconstrained |
119 | | - // inference variable. This means that we dropped information which could |
120 | | - // have been important. We handle this by instead returning the nested goals |
121 | | - // to the caller, where they are then handled. We only do so if we do not |
122 | | - // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly |
123 | | - // uplifting its nested goals. This is the case if the `shallow_certainty` is |
124 | | - // `Certainty::Yes`. |
125 | | - (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { |
126 | | - let goals = std::mem::take(&mut self.nested_goals); |
127 | | - // As we return all ambiguous nested goals, we can ignore the certainty |
128 | | - // returned by `self.try_evaluate_added_goals()`. |
129 | | - if goals.is_empty() { |
130 | | - assert!(matches!(goals_certainty, Certainty::Yes)); |
131 | | - } |
132 | | - ( |
133 | | - Certainty::Yes, |
134 | | - NestedNormalizationGoals( |
135 | | - goals.into_iter().map(|(s, g, _)| (s, g)).collect(), |
136 | | - ), |
137 | | - ) |
138 | | - } |
139 | | - _ => { |
140 | | - let certainty = shallow_certainty.and(goals_certainty); |
141 | | - (certainty, NestedNormalizationGoals::empty()) |
142 | | - } |
143 | | - }; |
144 | | - |
145 | | - if let Certainty::Maybe { |
146 | | - cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, |
147 | | - opaque_types_jank, |
148 | | - } = certainty |
149 | | - { |
150 | | - // If we have overflow, it's probable that we're substituting a type |
151 | | - // into itself infinitely and any partial substitutions in the query |
152 | | - // response are probably not useful anyways, so just return an empty |
153 | | - // query response. |
154 | | - // |
155 | | - // This may prevent us from potentially useful inference, e.g. |
156 | | - // 2 candidates, one ambiguous and one overflow, which both |
157 | | - // have the same inference constraints. |
158 | | - // |
159 | | - // Changing this to retain some constraints in the future |
160 | | - // won't be a breaking change, so this is good enough for now. |
161 | | - return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); |
162 | | - } |
163 | | - |
164 | | - let external_constraints = |
165 | | - self.compute_external_query_constraints(certainty, normalization_nested_goals); |
166 | | - let (var_values, mut external_constraints) = |
167 | | - eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); |
168 | | - |
169 | | - // Remove any trivial or duplicated region constraints once we've resolved regions |
170 | | - let mut unique = HashSet::default(); |
171 | | - external_constraints.region_constraints.retain(|outlives| { |
172 | | - outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) |
173 | | - }); |
174 | | - |
175 | | - let canonical = Canonicalizer::canonicalize_response( |
176 | | - self.delegate, |
177 | | - self.max_input_universe, |
178 | | - &mut Default::default(), |
179 | | - Response { |
180 | | - var_values, |
181 | | - certainty, |
182 | | - external_constraints: self.cx().mk_external_constraints(external_constraints), |
183 | | - }, |
184 | | - ); |
185 | | - |
186 | | - // HACK: We bail with overflow if the response would have too many non-region |
187 | | - // inference variables. This tends to only happen if we encounter a lot of |
188 | | - // ambiguous alias types which get replaced with fresh inference variables |
189 | | - // during generalization. This prevents hangs caused by an exponential blowup, |
190 | | - // see tests/ui/traits/next-solver/coherence-alias-hang.rs. |
191 | | - match self.current_goal_kind { |
192 | | - // We don't do so for `NormalizesTo` goals as we erased the expected term and |
193 | | - // bailing with overflow here would prevent us from detecting a type-mismatch, |
194 | | - // causing a coherence error in diesel, see #131969. We still bail with overflow |
195 | | - // when later returning from the parent AliasRelate goal. |
196 | | - CurrentGoalKind::NormalizesTo => {} |
197 | | - CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { |
198 | | - let num_non_region_vars = canonical |
199 | | - .variables |
200 | | - .iter() |
201 | | - .filter(|c| !c.is_region() && c.is_existential()) |
202 | | - .count(); |
203 | | - if num_non_region_vars > self.cx().recursion_limit() { |
204 | | - debug!(?num_non_region_vars, "too many inference variables -> overflow"); |
205 | | - return Ok(self.make_ambiguous_response_no_constraints( |
206 | | - MaybeCause::Overflow { |
207 | | - suggest_increasing_limit: true, |
208 | | - keep_constraints: false, |
209 | | - }, |
210 | | - OpaqueTypesJank::AllGood, |
211 | | - )); |
212 | | - } |
213 | | - } |
214 | | - } |
215 | | - |
216 | | - Ok(canonical) |
217 | | - } |
218 | | - |
219 | | - /// Constructs a totally unconstrained, ambiguous response to a goal. |
220 | | - /// |
221 | | - /// Take care when using this, since often it's useful to respond with |
222 | | - /// ambiguity but return constrained variables to guide inference. |
223 | | - pub(in crate::solve) fn make_ambiguous_response_no_constraints( |
224 | | - &self, |
225 | | - cause: MaybeCause, |
226 | | - opaque_types_jank: OpaqueTypesJank, |
227 | | - ) -> CanonicalResponse<I> { |
228 | | - response_no_constraints_raw( |
229 | | - self.cx(), |
230 | | - self.max_input_universe, |
231 | | - self.variables, |
232 | | - Certainty::Maybe { cause, opaque_types_jank }, |
233 | | - ) |
234 | | - } |
235 | | - |
236 | | - /// Computes the region constraints and *new* opaque types registered when |
237 | | - /// proving a goal. |
238 | | - /// |
239 | | - /// If an opaque was already constrained before proving this goal, then the |
240 | | - /// external constraints do not need to record that opaque, since if it is |
241 | | - /// further constrained by inference, that will be passed back in the var |
242 | | - /// values. |
243 | | - #[instrument(level = "trace", skip(self), ret)] |
244 | | - fn compute_external_query_constraints( |
245 | | - &self, |
246 | | - certainty: Certainty, |
247 | | - normalization_nested_goals: NestedNormalizationGoals<I>, |
248 | | - ) -> ExternalConstraintsData<I> { |
249 | | - // We only return region constraints once the certainty is `Yes`. This |
250 | | - // is necessary as we may drop nested goals on ambiguity, which may result |
251 | | - // in unconstrained inference variables in the region constraints. It also |
252 | | - // prevents us from emitting duplicate region constraints, avoiding some |
253 | | - // unnecessary work. This slightly weakens the leak check in case it uses |
254 | | - // region constraints from an ambiguous nested goal. This is tested in both |
255 | | - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and |
256 | | - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. |
257 | | - let region_constraints = if certainty == Certainty::Yes { |
258 | | - self.delegate.make_deduplicated_outlives_constraints() |
259 | | - } else { |
260 | | - Default::default() |
261 | | - }; |
262 | | - |
263 | | - // We only return *newly defined* opaque types from canonical queries. |
264 | | - // |
265 | | - // Constraints for any existing opaque types are already tracked by changes |
266 | | - // to the `var_values`. |
267 | | - let opaque_types = self |
268 | | - .delegate |
269 | | - .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); |
270 | | - |
271 | | - ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } |
272 | | - } |
273 | | - |
274 | 79 | /// After calling a canonical query, we apply the constraints returned |
275 | 80 | /// by the query using this function. |
276 | 81 | /// |
@@ -469,7 +274,7 @@ where |
469 | 274 | /// evaluating a goal. The `var_values` not only include the bound variables |
470 | 275 | /// of the query input, but also contain all unconstrained inference vars |
471 | 276 | /// created while evaluating this goal. |
472 | | -pub(in crate::solve) fn make_canonical_state<D, T, I>( |
| 277 | +pub fn make_canonical_state<D, T, I>( |
473 | 278 | delegate: &D, |
474 | 279 | var_values: &[I::GenericArg], |
475 | 280 | max_input_universe: ty::UniverseIndex, |
@@ -515,3 +320,22 @@ where |
515 | 320 | EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); |
516 | 321 | data |
517 | 322 | } |
| 323 | + |
| 324 | +pub fn response_no_constraints_raw<I: Interner>( |
| 325 | + cx: I, |
| 326 | + max_universe: ty::UniverseIndex, |
| 327 | + variables: I::CanonicalVarKinds, |
| 328 | + certainty: Certainty, |
| 329 | +) -> CanonicalResponse<I> { |
| 330 | + ty::Canonical { |
| 331 | + max_universe, |
| 332 | + variables, |
| 333 | + value: Response { |
| 334 | + var_values: ty::CanonicalVarValues::make_identity(cx, variables), |
| 335 | + // FIXME: maybe we should store the "no response" version in cx, like |
| 336 | + // we do for cx.types and stuff. |
| 337 | + external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), |
| 338 | + certainty, |
| 339 | + }, |
| 340 | + } |
| 341 | +} |
0 commit comments