rustc_next_trait_solver/canonical/
canonicalizer.rs1use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack};
2use rustc_type_ir::inherent::*;
3use rustc_type_ir::solve::{Goal, QueryInput};
4use rustc_type_ir::{
5 self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, Flags, InferCtxtLike,
6 Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
7};
8
9use crate::delegate::SolverDelegate;
10
11const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits(
13 TypeFlags::HAS_INFER.bits()
14 | TypeFlags::HAS_PLACEHOLDER.bits()
15 | TypeFlags::HAS_PARAM.bits()
16 | TypeFlags::HAS_FREE_REGIONS.bits()
17 | TypeFlags::HAS_RE_ERASED.bits(),
18)
19.unwrap();
20
21#[derive(Debug, Clone, Copy)]
22enum CanonicalizeInputKind {
23 ParamEnv,
27 Predicate,
29}
30
31#[derive(Debug, Clone, Copy)]
37enum CanonicalizeMode {
38 Input(CanonicalizeInputKind),
39 Response {
47 max_input_universe: ty::UniverseIndex,
57 },
58}
59
60pub(super) struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
61 delegate: &'a D,
62
63 canonicalize_mode: CanonicalizeMode,
65
66 variables: &'a mut Vec<I::GenericArg>,
68 var_kinds: Vec<CanonicalVarKind<I>>,
69 variable_lookup_table: HashMap<I::GenericArg, usize>,
70 sub_root_lookup_table: HashMap<ty::TyVid, usize>,
77
78 cache: HashMap<I::Ty, I::Ty>,
81}
82
83impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
84 pub(super) fn canonicalize_response<T: TypeFoldable<I>>(
85 delegate: &'a D,
86 max_input_universe: ty::UniverseIndex,
87 variables: &'a mut Vec<I::GenericArg>,
88 value: T,
89 ) -> ty::Canonical<I, T> {
90 let mut canonicalizer = Canonicalizer {
91 delegate,
92 canonicalize_mode: CanonicalizeMode::Response { max_input_universe },
93
94 variables,
95 variable_lookup_table: Default::default(),
96 sub_root_lookup_table: Default::default(),
97 var_kinds: Vec::new(),
98
99 cache: Default::default(),
100 };
101
102 let value = if value.has_type_flags(NEEDS_CANONICAL) {
103 value.fold_with(&mut canonicalizer)
104 } else {
105 value
106 };
107 debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
108 debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
109 let (max_universe, variables) = canonicalizer.finalize();
110 Canonical { max_universe, variables, value }
111 }
112 fn canonicalize_param_env(
113 delegate: &'a D,
114 variables: &'a mut Vec<I::GenericArg>,
115 param_env: I::ParamEnv,
116 ) -> (I::ParamEnv, HashMap<I::GenericArg, usize>, Vec<CanonicalVarKind<I>>) {
117 if !param_env.has_type_flags(NEEDS_CANONICAL) {
118 return (param_env, Default::default(), Vec::new());
119 }
120
121 if !param_env.has_non_region_infer() {
129 delegate.cx().canonical_param_env_cache_get_or_insert(
130 param_env,
131 || {
132 let mut variables = Vec::new();
133 let mut env_canonicalizer = Canonicalizer {
134 delegate,
135 canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
136
137 variables: &mut variables,
138 variable_lookup_table: Default::default(),
139 sub_root_lookup_table: Default::default(),
140 var_kinds: Vec::new(),
141
142 cache: Default::default(),
143 };
144 let param_env = param_env.fold_with(&mut env_canonicalizer);
145 debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
146 CanonicalParamEnvCacheEntry {
147 param_env,
148 variable_lookup_table: env_canonicalizer.variable_lookup_table,
149 var_kinds: env_canonicalizer.var_kinds,
150 variables,
151 }
152 },
153 |&CanonicalParamEnvCacheEntry {
154 param_env,
155 variables: ref cache_variables,
156 ref variable_lookup_table,
157 ref var_kinds,
158 }| {
159 debug_assert!(variables.is_empty());
160 variables.extend(cache_variables.iter().copied());
161 (param_env, variable_lookup_table.clone(), var_kinds.clone())
162 },
163 )
164 } else {
165 let mut env_canonicalizer = Canonicalizer {
166 delegate,
167 canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
168
169 variables,
170 variable_lookup_table: Default::default(),
171 sub_root_lookup_table: Default::default(),
172 var_kinds: Vec::new(),
173
174 cache: Default::default(),
175 };
176 let param_env = param_env.fold_with(&mut env_canonicalizer);
177 debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
178 (param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds)
179 }
180 }
181
182 pub(super) fn canonicalize_input<P: TypeFoldable<I>>(
191 delegate: &'a D,
192 variables: &'a mut Vec<I::GenericArg>,
193 input: QueryInput<I, P>,
194 ) -> ty::Canonical<I, QueryInput<I, P>> {
195 let (param_env, variable_lookup_table, var_kinds) =
197 Canonicalizer::canonicalize_param_env(delegate, variables, input.goal.param_env);
198 let mut rest_canonicalizer = Canonicalizer {
201 delegate,
202 canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate),
203
204 variables,
205 variable_lookup_table,
206 sub_root_lookup_table: Default::default(),
207 var_kinds,
208
209 cache: Default::default(),
215 };
216
217 let predicate = input.goal.predicate;
218 let predicate = if predicate.has_type_flags(NEEDS_CANONICAL) {
219 predicate.fold_with(&mut rest_canonicalizer)
220 } else {
221 predicate
222 };
223 let goal = Goal { param_env, predicate };
224
225 let predefined_opaques_in_body = input.predefined_opaques_in_body;
226 let predefined_opaques_in_body =
227 if input.predefined_opaques_in_body.has_type_flags(NEEDS_CANONICAL) {
228 predefined_opaques_in_body.fold_with(&mut rest_canonicalizer)
229 } else {
230 predefined_opaques_in_body
231 };
232
233 let value = QueryInput { goal, predefined_opaques_in_body };
234
235 debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
236 debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
237 let (max_universe, variables) = rest_canonicalizer.finalize();
238 Canonical { max_universe, variables, value }
239 }
240
241 fn get_or_insert_bound_var(
242 &mut self,
243 arg: impl Into<I::GenericArg>,
244 kind: CanonicalVarKind<I>,
245 ) -> ty::BoundVar {
246 let arg = arg.into();
249 let idx = if self.variables.len() > 16 {
250 if self.variable_lookup_table.is_empty() {
251 self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..));
252 }
253
254 *self.variable_lookup_table.entry(arg).or_insert_with(|| {
255 let var = self.variables.len();
256 self.variables.push(arg);
257 self.var_kinds.push(kind);
258 var
259 })
260 } else {
261 self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
262 let var = self.variables.len();
263 self.variables.push(arg);
264 self.var_kinds.push(kind);
265 var
266 })
267 };
268
269 ty::BoundVar::from(idx)
270 }
271
272 fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar {
273 let root_vid = self.delegate.sub_unification_table_root_var(vid);
274 let idx =
275 *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len());
276 ty::BoundVar::from(idx)
277 }
278
279 fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) {
280 let mut var_kinds = self.var_kinds;
281 match self.canonicalize_mode {
284 CanonicalizeMode::Input { .. } => {
286 debug_assert!(
287 var_kinds.iter().all(|var| var.universe() == ty::UniverseIndex::ROOT),
288 "expected all vars to be canonicalized in root universe: {var_kinds:#?}"
289 );
290 let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
291 (ty::UniverseIndex::ROOT, var_kinds)
292 }
293 CanonicalizeMode::Response { max_input_universe } => {
298 for var in var_kinds.iter_mut() {
299 let uv = var.universe();
300 let new_uv = ty::UniverseIndex::from(
301 uv.index().saturating_sub(max_input_universe.index()),
302 );
303 *var = var.with_updated_universe(new_uv);
304 }
305 let max_universe = var_kinds
306 .iter()
307 .map(|kind| kind.universe())
308 .max()
309 .unwrap_or(ty::UniverseIndex::ROOT);
310 let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
311 (max_universe, var_kinds)
312 }
313 }
314 }
315
316 fn inner_fold_ty(&mut self, t: I::Ty) -> I::Ty {
317 let kind = match t.kind() {
318 ty::Infer(i) => match i {
319 ty::TyVar(vid) => {
320 debug_assert_eq!(
321 self.delegate.opportunistic_resolve_ty_var(vid),
322 t,
323 "ty vid should have been resolved fully before canonicalization"
324 );
325
326 let sub_root = self.get_or_insert_sub_root(vid);
327 let ui = match self.canonicalize_mode {
328 CanonicalizeMode::Input { .. } => ty::UniverseIndex::ROOT,
329 CanonicalizeMode::Response { .. } => self
330 .delegate
331 .universe_of_ty(vid)
332 .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")),
333 };
334 CanonicalVarKind::Ty { ui, sub_root }
335 }
336 ty::IntVar(vid) => {
337 debug_assert_eq!(
338 self.delegate.opportunistic_resolve_int_var(vid),
339 t,
340 "ty vid should have been resolved fully before canonicalization"
341 );
342 CanonicalVarKind::Int
343 }
344 ty::FloatVar(vid) => {
345 debug_assert_eq!(
346 self.delegate.opportunistic_resolve_float_var(vid),
347 t,
348 "ty vid should have been resolved fully before canonicalization"
349 );
350 CanonicalVarKind::Float
351 }
352 ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
353 panic!("fresh vars not expected in canonicalization")
354 }
355 },
356 ty::Placeholder(placeholder) => match self.canonicalize_mode {
357 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
358 PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
359 ),
360 CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
361 },
362 ty::Param(_) => match self.canonicalize_mode {
363 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
364 PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
365 ),
366 CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"),
367 },
368 ty::Bool
369 | ty::Char
370 | ty::Int(_)
371 | ty::Uint(_)
372 | ty::Float(_)
373 | ty::Adt(_, _)
374 | ty::Foreign(_)
375 | ty::Str
376 | ty::Array(_, _)
377 | ty::Slice(_)
378 | ty::RawPtr(_, _)
379 | ty::Ref(_, _, _)
380 | ty::Pat(_, _)
381 | ty::FnDef(_, _)
382 | ty::FnPtr(..)
383 | ty::UnsafeBinder(_)
384 | ty::Dynamic(_, _)
385 | ty::Closure(..)
386 | ty::CoroutineClosure(..)
387 | ty::Coroutine(_, _)
388 | ty::CoroutineWitness(..)
389 | ty::Never
390 | ty::Tuple(_)
391 | ty::Alias(_, _)
392 | ty::Bound(_, _)
393 | ty::Error(_) => {
394 return if t.has_type_flags(NEEDS_CANONICAL) {
395 ensure_sufficient_stack(|| t.super_fold_with(self))
396 } else {
397 t
398 };
399 }
400 };
401
402 let var = self.get_or_insert_bound_var(t, kind);
403
404 Ty::new_canonical_bound(self.cx(), var)
405 }
406}
407
408impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicalizer<'_, D, I> {
409 fn cx(&self) -> I {
410 self.delegate.cx()
411 }
412
413 fn fold_region(&mut self, r: I::Region) -> I::Region {
414 let kind = match r.kind() {
415 ty::ReBound(..) => return r,
416
417 ty::ReStatic => match self.canonicalize_mode {
420 CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
421 CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
422 }
423 CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
424 | CanonicalizeMode::Response { .. } => return r,
425 },
426
427 ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
435 CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
436 CanonicalizeMode::Response { .. } => return r,
437 },
438
439 ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
440 CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
441 CanonicalizeMode::Response { .. } => {
442 panic!("unexpected region in response: {r:?}")
443 }
444 },
445
446 ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
447 CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
449 CanonicalizeMode::Response { max_input_universe } => {
450 if max_input_universe.can_name(placeholder.universe()) {
453 panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
454 }
455 CanonicalVarKind::PlaceholderRegion(placeholder)
456 }
457 },
458
459 ty::ReVar(vid) => {
460 debug_assert_eq!(
461 self.delegate.opportunistic_resolve_lt_var(vid),
462 r,
463 "region vid should have been resolved fully before canonicalization"
464 );
465 match self.canonicalize_mode {
466 CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
467 CanonicalizeMode::Response { .. } => {
468 CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
469 }
470 }
471 }
472 };
473
474 let var = self.get_or_insert_bound_var(r, kind);
475
476 Region::new_canonical_bound(self.cx(), var)
477 }
478
479 fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
480 if let Some(&ty) = self.cache.get(&t) {
481 ty
482 } else {
483 let res = self.inner_fold_ty(t);
484 let old = self.cache.insert(t, res);
485 assert_eq!(old, None);
486 res
487 }
488 }
489
490 fn fold_const(&mut self, c: I::Const) -> I::Const {
491 let kind = match c.kind() {
492 ty::ConstKind::Infer(i) => match i {
493 ty::InferConst::Var(vid) => {
494 debug_assert_eq!(
495 self.delegate.opportunistic_resolve_ct_var(vid),
496 c,
497 "const vid should have been resolved fully before canonicalization"
498 );
499
500 match self.canonicalize_mode {
501 CanonicalizeMode::Input { .. } => {
502 CanonicalVarKind::Const(ty::UniverseIndex::ROOT)
503 }
504 CanonicalizeMode::Response { .. } => {
505 CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
506 }
507 }
508 }
509 ty::InferConst::Fresh(_) => todo!(),
510 },
511 ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
512 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
513 PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
514 ),
515 CanonicalizeMode::Response { .. } => {
516 CanonicalVarKind::PlaceholderConst(placeholder)
517 }
518 },
519 ty::ConstKind::Param(_) => match self.canonicalize_mode {
520 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
521 PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
522 ),
523 CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
524 },
525 ty::ConstKind::Bound(_, _)
527 | ty::ConstKind::Unevaluated(_)
528 | ty::ConstKind::Value(_)
529 | ty::ConstKind::Error(_)
530 | ty::ConstKind::Expr(_) => {
531 return if c.has_type_flags(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c };
532 }
533 };
534
535 let var = self.get_or_insert_bound_var(c, kind);
536
537 Const::new_canonical_bound(self.cx(), var)
538 }
539
540 fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
541 if p.flags().intersects(NEEDS_CANONICAL) { p.super_fold_with(self) } else { p }
542 }
543
544 fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
545 match self.canonicalize_mode {
546 CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
547 | CanonicalizeMode::Response { max_input_universe: _ } => {}
548 CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
549 panic!("erasing 'static in env")
550 }
551 }
552 if c.flags().intersects(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c }
553 }
554}