rustc_next_trait_solver/
canonicalizer.rs

1use std::cmp::Ordering;
2
3use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack};
4use rustc_type_ir::inherent::*;
5use rustc_type_ir::solve::{Goal, QueryInput};
6use rustc_type_ir::{
7    self as ty, Canonical, CanonicalTyVarKind, CanonicalVarKind, Flags, InferCtxtLike, Interner,
8    TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
9};
10
11use crate::delegate::SolverDelegate;
12
13/// Does this have infer/placeholder/param, free regions or ReErased?
14const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits(
15    TypeFlags::HAS_INFER.bits()
16        | TypeFlags::HAS_PLACEHOLDER.bits()
17        | TypeFlags::HAS_PARAM.bits()
18        | TypeFlags::HAS_FREE_REGIONS.bits()
19        | TypeFlags::HAS_RE_ERASED.bits(),
20)
21.unwrap();
22
23/// Whether we're canonicalizing a query input or the query response.
24///
25/// When canonicalizing an input we're in the context of the caller
26/// while canonicalizing the response happens in the context of the
27/// query.
28#[derive(Debug, Clone, Copy)]
29enum CanonicalizeMode {
30    /// When canonicalizing the `param_env`, we keep `'static` as merging
31    /// trait candidates relies on it when deciding whether a where-bound
32    /// is trivial.
33    Input { keep_static: bool },
34    /// FIXME: We currently return region constraints referring to
35    /// placeholders and inference variables from a binder instantiated
36    /// inside of the query.
37    ///
38    /// In the long term we should eagerly deal with these constraints
39    /// inside of the query and only propagate constraints which are
40    /// actually nameable by the caller.
41    Response {
42        /// The highest universe nameable by the caller.
43        ///
44        /// All variables in a universe nameable by the caller get mapped
45        /// to the root universe in the response and then mapped back to
46        /// their correct universe when applying the query response in the
47        /// context of the caller.
48        ///
49        /// This doesn't work for universes created inside of the query so
50        /// we do remember their universe in the response.
51        max_input_universe: ty::UniverseIndex,
52    },
53}
54
55pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
56    delegate: &'a D,
57
58    // Immutable field.
59    canonicalize_mode: CanonicalizeMode,
60
61    // Mutable fields.
62    variables: &'a mut Vec<I::GenericArg>,
63    var_kinds: Vec<CanonicalVarKind<I>>,
64    variable_lookup_table: HashMap<I::GenericArg, usize>,
65    binder_index: ty::DebruijnIndex,
66
67    /// We only use the debruijn index during lookup. We don't need to
68    /// track the `variables` as each generic arg only results in a single
69    /// bound variable regardless of how many times it is encountered.
70    cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>,
71}
72
73impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
74    pub fn canonicalize_response<T: TypeFoldable<I>>(
75        delegate: &'a D,
76        max_input_universe: ty::UniverseIndex,
77        variables: &'a mut Vec<I::GenericArg>,
78        value: T,
79    ) -> ty::Canonical<I, T> {
80        let mut canonicalizer = Canonicalizer {
81            delegate,
82            canonicalize_mode: CanonicalizeMode::Response { max_input_universe },
83
84            variables,
85            variable_lookup_table: Default::default(),
86            var_kinds: Vec::new(),
87            binder_index: ty::INNERMOST,
88
89            cache: Default::default(),
90        };
91
92        let value = if value.has_type_flags(NEEDS_CANONICAL) {
93            value.fold_with(&mut canonicalizer)
94        } else {
95            value
96        };
97        debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
98        debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
99        let (max_universe, variables) = canonicalizer.finalize();
100        Canonical { max_universe, variables, value }
101    }
102
103    /// When canonicalizing query inputs, we keep `'static` in the `param_env`
104    /// but erase it everywhere else. We generally don't want to depend on region
105    /// identity, so while it should not matter whether `'static` is kept in the
106    /// value or opaque type storage as well, this prevents us from accidentally
107    /// relying on it in the future.
108    ///
109    /// We want to keep the option of canonicalizing `'static` to an existential
110    /// variable in the future by changing the way we detect global where-bounds.
111    pub fn canonicalize_input<P: TypeFoldable<I>>(
112        delegate: &'a D,
113        variables: &'a mut Vec<I::GenericArg>,
114        input: QueryInput<I, P>,
115    ) -> ty::Canonical<I, QueryInput<I, P>> {
116        // First canonicalize the `param_env` while keeping `'static`
117        let mut env_canonicalizer = Canonicalizer {
118            delegate,
119            canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
120
121            variables,
122            variable_lookup_table: Default::default(),
123            var_kinds: Vec::new(),
124            binder_index: ty::INNERMOST,
125
126            cache: Default::default(),
127        };
128
129        let param_env = input.goal.param_env;
130        let param_env = if param_env.has_type_flags(NEEDS_CANONICAL) {
131            param_env.fold_with(&mut env_canonicalizer)
132        } else {
133            param_env
134        };
135
136        debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
137        // Then canonicalize the rest of the input without keeping `'static`
138        // while *mostly* reusing the canonicalizer from above.
139        let mut rest_canonicalizer = Canonicalizer {
140            delegate,
141            canonicalize_mode: CanonicalizeMode::Input { keep_static: false },
142
143            variables: env_canonicalizer.variables,
144            // We're able to reuse the `variable_lookup_table` as whether or not
145            // it already contains an entry for `'static` does not matter.
146            variable_lookup_table: env_canonicalizer.variable_lookup_table,
147            var_kinds: env_canonicalizer.var_kinds,
148            binder_index: ty::INNERMOST,
149
150            // We do not reuse the cache as it may contain entries whose canonicalized
151            // value contains `'static`. While we could alternatively handle this by
152            // checking for `'static` when using cached entries, this does not
153            // feel worth the effort. I do not expect that a `ParamEnv` will ever
154            // contain large enough types for caching to be necessary.
155            cache: Default::default(),
156        };
157
158        let predicate = input.goal.predicate;
159        let predicate = if predicate.has_type_flags(NEEDS_CANONICAL) {
160            predicate.fold_with(&mut rest_canonicalizer)
161        } else {
162            predicate
163        };
164        let goal = Goal { param_env, predicate };
165
166        let predefined_opaques_in_body = input.predefined_opaques_in_body;
167        let predefined_opaques_in_body =
168            if input.predefined_opaques_in_body.has_type_flags(NEEDS_CANONICAL) {
169                predefined_opaques_in_body.fold_with(&mut rest_canonicalizer)
170            } else {
171                predefined_opaques_in_body
172            };
173
174        let value = QueryInput { goal, predefined_opaques_in_body };
175
176        debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
177        debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
178        let (max_universe, variables) = rest_canonicalizer.finalize();
179        Canonical { max_universe, variables, value }
180    }
181
182    fn get_or_insert_bound_var(
183        &mut self,
184        arg: impl Into<I::GenericArg>,
185        kind: CanonicalVarKind<I>,
186    ) -> ty::BoundVar {
187        // FIXME: 16 is made up and arbitrary. We should look at some
188        // perf data here.
189        let arg = arg.into();
190        let idx = if self.variables.len() > 16 {
191            if self.variable_lookup_table.is_empty() {
192                self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..));
193            }
194
195            *self.variable_lookup_table.entry(arg).or_insert_with(|| {
196                let var = self.variables.len();
197                self.variables.push(arg);
198                self.var_kinds.push(kind);
199                var
200            })
201        } else {
202            self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
203                let var = self.variables.len();
204                self.variables.push(arg);
205                self.var_kinds.push(kind);
206                var
207            })
208        };
209
210        ty::BoundVar::from(idx)
211    }
212
213    fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) {
214        let mut var_kinds = self.var_kinds;
215        // See the rustc-dev-guide section about how we deal with universes
216        // during canonicalization in the new solver.
217        match self.canonicalize_mode {
218            // We try to deduplicate as many query calls as possible and hide
219            // all information which should not matter for the solver.
220            //
221            // For this we compress universes as much as possible.
222            CanonicalizeMode::Input { .. } => {}
223            // When canonicalizing a response we map a universes already entered
224            // by the caller to the root universe and only return useful universe
225            // information for placeholders and inference variables created inside
226            // of the query.
227            CanonicalizeMode::Response { max_input_universe } => {
228                for var in var_kinds.iter_mut() {
229                    let uv = var.universe();
230                    let new_uv = ty::UniverseIndex::from(
231                        uv.index().saturating_sub(max_input_universe.index()),
232                    );
233                    *var = var.with_updated_universe(new_uv);
234                }
235                let max_universe = var_kinds
236                    .iter()
237                    .map(|kind| kind.universe())
238                    .max()
239                    .unwrap_or(ty::UniverseIndex::ROOT);
240
241                let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
242                return (max_universe, var_kinds);
243            }
244        }
245
246        // Given a `var_kinds` with existentials `En` and universals `Un` in
247        // universes `n`, this algorithm compresses them in place so that:
248        //
249        // - the new universe indices are as small as possible
250        // - we create a new universe if we would otherwise
251        //   1. put existentials from a different universe into the same one
252        //   2. put a placeholder in the same universe as an existential which cannot name it
253        //
254        // Let's walk through an example:
255        // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0
256        // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1
257        // - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2
258        // - var_kinds: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5
259        // - var_kinds: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
260        // - var_kinds: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
261        //
262        // This algorithm runs in `O(mn)` where `n` is the number of different universes and
263        // `m` the number of variables. This should be fine as both are expected to be small.
264        let mut curr_compressed_uv = ty::UniverseIndex::ROOT;
265        let mut existential_in_new_uv = None;
266        let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
267        while let Some(orig_uv) = next_orig_uv.take() {
268            let mut update_uv = |var: &mut CanonicalVarKind<I>, orig_uv, is_existential| {
269                let uv = var.universe();
270                match uv.cmp(&orig_uv) {
271                    Ordering::Less => (), // Already updated
272                    Ordering::Equal => {
273                        if is_existential {
274                            if existential_in_new_uv.is_some_and(|uv| uv < orig_uv) {
275                                // Condition 1.
276                                //
277                                // We already put an existential from a outer universe
278                                // into the current compressed universe, so we need to
279                                // create a new one.
280                                curr_compressed_uv = curr_compressed_uv.next_universe();
281                            }
282
283                            // `curr_compressed_uv` will now contain an existential from
284                            // `orig_uv`. Trying to canonicalizing an existential from
285                            // a higher universe has to therefore use a new compressed
286                            // universe.
287                            existential_in_new_uv = Some(orig_uv);
288                        } else if existential_in_new_uv.is_some() {
289                            // Condition 2.
290                            //
291                            //  `var` is a placeholder from a universe which is not nameable
292                            // by an existential which we already put into the compressed
293                            // universe `curr_compressed_uv`. We therefore have to create a
294                            // new universe for `var`.
295                            curr_compressed_uv = curr_compressed_uv.next_universe();
296                            existential_in_new_uv = None;
297                        }
298
299                        *var = var.with_updated_universe(curr_compressed_uv);
300                    }
301                    Ordering::Greater => {
302                        // We can ignore this variable in this iteration. We only look at
303                        // universes which actually occur in the input for performance.
304                        //
305                        // For this we set `next_orig_uv` to the next smallest, not yet compressed,
306                        // universe of the input.
307                        if next_orig_uv.is_none_or(|curr_next_uv| uv.cannot_name(curr_next_uv)) {
308                            next_orig_uv = Some(uv);
309                        }
310                    }
311                }
312            };
313
314            // For each universe which occurs in the input, we first iterate over all
315            // placeholders and then over all inference variables.
316            //
317            // Whenever we compress the universe of a placeholder, no existential with
318            // an already compressed universe can name that placeholder.
319            for is_existential in [false, true] {
320                for var in var_kinds.iter_mut() {
321                    // We simply put all regions from the input into the highest
322                    // compressed universe, so we only deal with them at the end.
323                    if !var.is_region() {
324                        if is_existential == var.is_existential() {
325                            update_uv(var, orig_uv, is_existential)
326                        }
327                    }
328                }
329            }
330        }
331
332        // We put all regions into a separate universe.
333        let mut first_region = true;
334        for var in var_kinds.iter_mut() {
335            if var.is_region() {
336                if first_region {
337                    first_region = false;
338                    curr_compressed_uv = curr_compressed_uv.next_universe();
339                }
340                debug_assert!(var.is_existential());
341                *var = var.with_updated_universe(curr_compressed_uv);
342            }
343        }
344
345        let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
346        (curr_compressed_uv, var_kinds)
347    }
348
349    fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty {
350        let kind = match t.kind() {
351            ty::Infer(i) => match i {
352                ty::TyVar(vid) => {
353                    debug_assert_eq!(
354                        self.delegate.opportunistic_resolve_ty_var(vid),
355                        t,
356                        "ty vid should have been resolved fully before canonicalization"
357                    );
358
359                    CanonicalVarKind::Ty(CanonicalTyVarKind::General(
360                        self.delegate
361                            .universe_of_ty(vid)
362                            .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")),
363                    ))
364                }
365                ty::IntVar(vid) => {
366                    debug_assert_eq!(
367                        self.delegate.opportunistic_resolve_int_var(vid),
368                        t,
369                        "ty vid should have been resolved fully before canonicalization"
370                    );
371                    CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
372                }
373                ty::FloatVar(vid) => {
374                    debug_assert_eq!(
375                        self.delegate.opportunistic_resolve_float_var(vid),
376                        t,
377                        "ty vid should have been resolved fully before canonicalization"
378                    );
379                    CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
380                }
381                ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
382                    panic!("fresh vars not expected in canonicalization")
383                }
384            },
385            ty::Placeholder(placeholder) => match self.canonicalize_mode {
386                CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
387                    PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
388                ),
389                CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
390            },
391            ty::Param(_) => match self.canonicalize_mode {
392                CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
393                    PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
394                ),
395                CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"),
396            },
397            ty::Bool
398            | ty::Char
399            | ty::Int(_)
400            | ty::Uint(_)
401            | ty::Float(_)
402            | ty::Adt(_, _)
403            | ty::Foreign(_)
404            | ty::Str
405            | ty::Array(_, _)
406            | ty::Slice(_)
407            | ty::RawPtr(_, _)
408            | ty::Ref(_, _, _)
409            | ty::Pat(_, _)
410            | ty::FnDef(_, _)
411            | ty::FnPtr(..)
412            | ty::UnsafeBinder(_)
413            | ty::Dynamic(_, _, _)
414            | ty::Closure(..)
415            | ty::CoroutineClosure(..)
416            | ty::Coroutine(_, _)
417            | ty::CoroutineWitness(..)
418            | ty::Never
419            | ty::Tuple(_)
420            | ty::Alias(_, _)
421            | ty::Bound(_, _)
422            | ty::Error(_) => {
423                return if t.has_type_flags(NEEDS_CANONICAL) {
424                    ensure_sufficient_stack(|| t.super_fold_with(self))
425                } else {
426                    t
427                };
428            }
429        };
430
431        let var = self.get_or_insert_bound_var(t, kind);
432
433        Ty::new_anon_bound(self.cx(), self.binder_index, var)
434    }
435}
436
437impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicalizer<'_, D, I> {
438    fn cx(&self) -> I {
439        self.delegate.cx()
440    }
441
442    fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
443    where
444        T: TypeFoldable<I>,
445    {
446        self.binder_index.shift_in(1);
447        let t = t.super_fold_with(self);
448        self.binder_index.shift_out(1);
449        t
450    }
451
452    fn fold_region(&mut self, r: I::Region) -> I::Region {
453        let kind = match r.kind() {
454            ty::ReBound(..) => return r,
455
456            // We don't canonicalize `ReStatic` in the `param_env` as we use it
457            // when checking whether a `ParamEnv` candidate is global.
458            ty::ReStatic => match self.canonicalize_mode {
459                CanonicalizeMode::Input { keep_static: false } => {
460                    CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
461                }
462                CanonicalizeMode::Input { keep_static: true }
463                | CanonicalizeMode::Response { .. } => return r,
464            },
465
466            // `ReErased` should only be encountered in the hidden
467            // type of an opaque for regions that are ignored for the purposes of
468            // captures.
469            //
470            // FIXME: We should investigate the perf implications of not uniquifying
471            // `ReErased`. We may be able to short-circuit registering region
472            // obligations if we encounter a `ReErased` on one side, for example.
473            ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
474                CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
475                CanonicalizeMode::Response { .. } => return r,
476            },
477
478            ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
479                CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
480                CanonicalizeMode::Response { .. } => {
481                    panic!("unexpected region in response: {r:?}")
482                }
483            },
484
485            ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
486                // We canonicalize placeholder regions as existentials in query inputs.
487                CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
488                CanonicalizeMode::Response { max_input_universe } => {
489                    // If we have a placeholder region inside of a query, it must be from
490                    // a new universe.
491                    if max_input_universe.can_name(placeholder.universe()) {
492                        panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
493                    }
494                    CanonicalVarKind::PlaceholderRegion(placeholder)
495                }
496            },
497
498            ty::ReVar(vid) => {
499                debug_assert_eq!(
500                    self.delegate.opportunistic_resolve_lt_var(vid),
501                    r,
502                    "region vid should have been resolved fully before canonicalization"
503                );
504                match self.canonicalize_mode {
505                    CanonicalizeMode::Input { keep_static: _ } => {
506                        CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
507                    }
508                    CanonicalizeMode::Response { .. } => {
509                        CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
510                    }
511                }
512            }
513        };
514
515        let var = self.get_or_insert_bound_var(r, kind);
516
517        Region::new_anon_bound(self.cx(), self.binder_index, var)
518    }
519
520    fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
521        if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
522            ty
523        } else {
524            let res = self.cached_fold_ty(t);
525            let old = self.cache.insert((self.binder_index, t), res);
526            assert_eq!(old, None);
527            res
528        }
529    }
530
531    fn fold_const(&mut self, c: I::Const) -> I::Const {
532        let kind = match c.kind() {
533            ty::ConstKind::Infer(i) => match i {
534                ty::InferConst::Var(vid) => {
535                    debug_assert_eq!(
536                        self.delegate.opportunistic_resolve_ct_var(vid),
537                        c,
538                        "const vid should have been resolved fully before canonicalization"
539                    );
540                    CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
541                }
542                ty::InferConst::Fresh(_) => todo!(),
543            },
544            ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
545                CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
546                    PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
547                ),
548                CanonicalizeMode::Response { .. } => {
549                    CanonicalVarKind::PlaceholderConst(placeholder)
550                }
551            },
552            ty::ConstKind::Param(_) => match self.canonicalize_mode {
553                CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
554                    PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
555                ),
556                CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
557            },
558            // FIXME: See comment above -- we could fold the region separately or something.
559            ty::ConstKind::Bound(_, _)
560            | ty::ConstKind::Unevaluated(_)
561            | ty::ConstKind::Value(_)
562            | ty::ConstKind::Error(_)
563            | ty::ConstKind::Expr(_) => {
564                return if c.has_type_flags(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c };
565            }
566        };
567
568        let var = self.get_or_insert_bound_var(c, kind);
569
570        Const::new_anon_bound(self.cx(), self.binder_index, var)
571    }
572
573    fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
574        if p.flags().intersects(NEEDS_CANONICAL) { p.super_fold_with(self) } else { p }
575    }
576
577    fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
578        match self.canonicalize_mode {
579            CanonicalizeMode::Input { keep_static: true }
580            | CanonicalizeMode::Response { max_input_universe: _ } => {}
581            CanonicalizeMode::Input { keep_static: false } => {
582                panic!("erasing 'static in env")
583            }
584        }
585        if c.flags().intersects(NEEDS_CANONICAL) { c.super_fold_with(self) } else { c }
586    }
587}