rustc_type_ir/
fast_reject.rs

1use std::fmt::Debug;
2use std::hash::Hash;
3use std::iter;
4use std::marker::PhantomData;
5
6use rustc_ast_ir::Mutability;
7#[cfg(feature = "nightly")]
8use rustc_data_structures::fingerprint::Fingerprint;
9#[cfg(feature = "nightly")]
10use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
11#[cfg(feature = "nightly")]
12use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
13
14use crate::inherent::*;
15use crate::visit::TypeVisitableExt as _;
16use crate::{self as ty, Interner};
17
18/// See `simplify_type`.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20#[cfg_attr(
21    feature = "nightly",
22    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
23)]
24pub enum SimplifiedType<DefId> {
25    Bool,
26    Char,
27    Int(ty::IntTy),
28    Uint(ty::UintTy),
29    Float(ty::FloatTy),
30    Adt(DefId),
31    Foreign(DefId),
32    Str,
33    Array,
34    Slice,
35    Ref(Mutability),
36    Ptr(Mutability),
37    Never,
38    Tuple(usize),
39    /// A trait object, all of whose components are markers
40    /// (e.g., `dyn Send + Sync`).
41    MarkerTraitObject,
42    Trait(DefId),
43    Closure(DefId),
44    Coroutine(DefId),
45    CoroutineWitness(DefId),
46    Function(usize),
47    UnsafeBinder,
48    Placeholder,
49    Error,
50}
51
52#[cfg(feature = "nightly")]
53impl<HCX: Clone, DefId: HashStable<HCX>> ToStableHashKey<HCX> for SimplifiedType<DefId> {
54    type KeyType = Fingerprint;
55
56    #[inline]
57    fn to_stable_hash_key(&self, hcx: &HCX) -> Fingerprint {
58        let mut hasher = StableHasher::new();
59        let mut hcx: HCX = hcx.clone();
60        self.hash_stable(&mut hcx, &mut hasher);
61        hasher.finish()
62    }
63}
64
65/// Generic parameters are pretty much just bound variables, e.g.
66/// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as
67/// `for<'a, T> fn(&'a T) -> u32`.
68///
69/// Typecheck of `foo` has to succeed for all possible generic arguments, so
70/// during typeck, we have to treat its generic parameters as if they
71/// were placeholders.
72///
73/// But when calling `foo` we only have to provide a specific generic argument.
74/// In that case the generic parameters are instantiated with inference variables.
75/// As we use `simplify_type` before that instantiation happens, we just treat
76/// generic parameters as if they were inference variables in that case.
77#[derive(PartialEq, Eq, Debug, Clone, Copy)]
78pub enum TreatParams {
79    /// Treat parameters as infer vars. This is the correct mode for caching
80    /// an impl's type for lookup.
81    InstantiateWithInfer,
82    /// Treat parameters as placeholders in the given environment. This is the
83    /// correct mode for *lookup*, as during candidate selection.
84    ///
85    /// This also treats projections with inference variables as infer vars
86    /// since they could be further normalized.
87    AsRigid,
88}
89
90/// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists.
91///
92/// **This function should only be used if you need to store or retrieve the type from some
93/// hashmap. If you want to quickly decide whether two types may unify, use the [DeepRejectCtxt]
94/// instead.**
95///
96/// The idea is to get something simple that we can use to quickly decide if two types could unify,
97/// for example during method lookup. If this function returns `Some(x)` it can only unify with
98/// types for which this method returns either `Some(x)` as well or `None`.
99///
100/// A special case here are parameters and projections, which are only injective
101/// if they are treated as placeholders.
102///
103/// For example when storing impls based on their simplified self type, we treat
104/// generic parameters as if they were inference variables. We must not simplify them here,
105/// as they can unify with any other type.
106///
107/// With projections we have to be even more careful, as treating them as placeholders
108/// is only correct if they are fully normalized.
109///
110/// ¹ meaning that if the outermost layers are different, then the whole types are also different.
111pub fn simplify_type<I: Interner>(
112    cx: I,
113    ty: I::Ty,
114    treat_params: TreatParams,
115) -> Option<SimplifiedType<I::DefId>> {
116    match ty.kind() {
117        ty::Bool => Some(SimplifiedType::Bool),
118        ty::Char => Some(SimplifiedType::Char),
119        ty::Int(int_type) => Some(SimplifiedType::Int(int_type)),
120        ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)),
121        ty::Float(float_type) => Some(SimplifiedType::Float(float_type)),
122        ty::Adt(def, _) => Some(SimplifiedType::Adt(def.def_id())),
123        ty::Str => Some(SimplifiedType::Str),
124        ty::Array(..) => Some(SimplifiedType::Array),
125        ty::Slice(..) => Some(SimplifiedType::Slice),
126        ty::Pat(ty, ..) => simplify_type(cx, ty, treat_params),
127        ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)),
128        ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() {
129            Some(principal_def_id) if !cx.trait_is_auto(principal_def_id) => {
130                Some(SimplifiedType::Trait(principal_def_id))
131            }
132            _ => Some(SimplifiedType::MarkerTraitObject),
133        },
134        ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)),
135        ty::FnDef(def_id, _) | ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => {
136            Some(SimplifiedType::Closure(def_id))
137        }
138        ty::Coroutine(def_id, _) => Some(SimplifiedType::Coroutine(def_id)),
139        ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id)),
140        ty::Never => Some(SimplifiedType::Never),
141        ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())),
142        ty::FnPtr(sig_tys, _hdr) => {
143            Some(SimplifiedType::Function(sig_tys.skip_binder().inputs().len()))
144        }
145        ty::UnsafeBinder(_) => Some(SimplifiedType::UnsafeBinder),
146        ty::Placeholder(..) => Some(SimplifiedType::Placeholder),
147        ty::Param(_) => match treat_params {
148            TreatParams::AsRigid => Some(SimplifiedType::Placeholder),
149            TreatParams::InstantiateWithInfer => None,
150        },
151        ty::Alias(..) => match treat_params {
152            // When treating `ty::Param` as a placeholder, projections also
153            // don't unify with anything else as long as they are fully normalized.
154            // FIXME(-Znext-solver): Can remove this `if` and always simplify to `Placeholder`
155            // when the new solver is enabled by default.
156            TreatParams::AsRigid if !ty.has_non_region_infer() => Some(SimplifiedType::Placeholder),
157            TreatParams::AsRigid | TreatParams::InstantiateWithInfer => None,
158        },
159        ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)),
160        ty::Error(_) => Some(SimplifiedType::Error),
161        ty::Bound(..) | ty::Infer(_) => None,
162    }
163}
164
165impl<DefId> SimplifiedType<DefId> {
166    pub fn def(self) -> Option<DefId> {
167        match self {
168            SimplifiedType::Adt(d)
169            | SimplifiedType::Foreign(d)
170            | SimplifiedType::Trait(d)
171            | SimplifiedType::Closure(d)
172            | SimplifiedType::Coroutine(d)
173            | SimplifiedType::CoroutineWitness(d) => Some(d),
174            _ => None,
175        }
176    }
177}
178
179/// Given generic arguments, could they be unified after
180/// replacing parameters with inference variables or placeholders.
181/// This behavior is toggled using the const generics.
182///
183/// We use this to quickly reject impl/wc candidates without needing
184/// to instantiate generic arguments/having to enter a probe.
185///
186/// We also use this function during coherence. For coherence the
187/// impls only have to overlap for some value, so we treat parameters
188/// on both sides like inference variables.
189#[derive(Debug, Clone, Copy)]
190pub struct DeepRejectCtxt<
191    I: Interner,
192    const INSTANTIATE_LHS_WITH_INFER: bool,
193    const INSTANTIATE_RHS_WITH_INFER: bool,
194> {
195    _interner: PhantomData<I>,
196}
197
198impl<I: Interner> DeepRejectCtxt<I, false, false> {
199    /// Treat parameters in both the lhs and the rhs as rigid.
200    pub fn relate_rigid_rigid(_interner: I) -> DeepRejectCtxt<I, false, false> {
201        DeepRejectCtxt { _interner: PhantomData }
202    }
203}
204
205impl<I: Interner> DeepRejectCtxt<I, true, true> {
206    /// Treat parameters in both the lhs and the rhs as infer vars.
207    pub fn relate_infer_infer(_interner: I) -> DeepRejectCtxt<I, true, true> {
208        DeepRejectCtxt { _interner: PhantomData }
209    }
210}
211
212impl<I: Interner> DeepRejectCtxt<I, false, true> {
213    /// Treat parameters in the lhs as rigid, and in rhs as infer vars.
214    pub fn relate_rigid_infer(_interner: I) -> DeepRejectCtxt<I, false, true> {
215        DeepRejectCtxt { _interner: PhantomData }
216    }
217}
218
219impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_WITH_INFER: bool>
220    DeepRejectCtxt<I, INSTANTIATE_LHS_WITH_INFER, INSTANTIATE_RHS_WITH_INFER>
221{
222    // Quite arbitrary. Large enough to only affect a very tiny amount of impls/crates
223    // and small enough to prevent hangs.
224    const STARTING_DEPTH: usize = 8;
225
226    pub fn args_may_unify(
227        self,
228        obligation_args: I::GenericArgs,
229        impl_args: I::GenericArgs,
230    ) -> bool {
231        self.args_may_unify_inner(obligation_args, impl_args, Self::STARTING_DEPTH)
232    }
233
234    pub fn types_may_unify(self, lhs: I::Ty, rhs: I::Ty) -> bool {
235        if lhs == rhs {
236            return true;
237        }
238        self.types_may_unify_inner(lhs, rhs, Self::STARTING_DEPTH)
239    }
240
241    fn args_may_unify_inner(
242        self,
243        obligation_args: I::GenericArgs,
244        impl_args: I::GenericArgs,
245        depth: usize,
246    ) -> bool {
247        // No need to decrement the depth here as this function is only
248        // recursively reachable via `types_may_unify_inner` which already
249        // increments the depth for us.
250        iter::zip(obligation_args.iter(), impl_args.iter()).all(|(obl, imp)| {
251            match (obl.kind(), imp.kind()) {
252                // We don't fast reject based on regions.
253                (ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_)) => true,
254                (ty::GenericArgKind::Type(obl), ty::GenericArgKind::Type(imp)) => {
255                    self.types_may_unify_inner(obl, imp, depth)
256                }
257                (ty::GenericArgKind::Const(obl), ty::GenericArgKind::Const(imp)) => {
258                    self.consts_may_unify_inner(obl, imp)
259                }
260                _ => panic!("kind mismatch: {obl:?} {imp:?}"),
261            }
262        })
263    }
264
265    fn types_may_unify_inner(self, lhs: I::Ty, rhs: I::Ty, depth: usize) -> bool {
266        match rhs.kind() {
267            // Start by checking whether the `rhs` type may unify with
268            // pretty much everything. Just return `true` in that case.
269            ty::Param(_) => {
270                if INSTANTIATE_RHS_WITH_INFER {
271                    return true;
272                }
273            }
274            ty::Error(_) | ty::Alias(..) | ty::Bound(..) => return true,
275            ty::Infer(var) => return self.var_and_ty_may_unify(var, lhs),
276
277            // These types only unify with inference variables or their own
278            // variant.
279            ty::Bool
280            | ty::Char
281            | ty::Int(_)
282            | ty::Uint(_)
283            | ty::Float(_)
284            | ty::Adt(..)
285            | ty::Str
286            | ty::Array(..)
287            | ty::Slice(..)
288            | ty::RawPtr(..)
289            | ty::Dynamic(..)
290            | ty::Pat(..)
291            | ty::Ref(..)
292            | ty::Never
293            | ty::Tuple(..)
294            | ty::FnDef(..)
295            | ty::FnPtr(..)
296            | ty::Closure(..)
297            | ty::CoroutineClosure(..)
298            | ty::Coroutine(..)
299            | ty::CoroutineWitness(..)
300            | ty::Foreign(_)
301            | ty::Placeholder(_)
302            | ty::UnsafeBinder(_) => {}
303        };
304
305        // The type system needs to support exponentially large types
306        // as long as they are self-similar. While most other folders
307        // use caching to handle them, this folder exists purely as a
308        // perf optimization and is incredibly hot. In pretty much all
309        // uses checking the cache is slower than simply recursing, so
310        // we instead just add an arbitrary depth cutoff.
311        //
312        // We only decrement the depth here as the match on `rhs`
313        // does not recurse.
314        let Some(depth) = depth.checked_sub(1) else {
315            return true;
316        };
317
318        // For purely rigid types, use structural equivalence.
319        match lhs.kind() {
320            ty::Ref(_, lhs_ty, lhs_mutbl) => match rhs.kind() {
321                ty::Ref(_, rhs_ty, rhs_mutbl) => {
322                    lhs_mutbl == rhs_mutbl && self.types_may_unify_inner(lhs_ty, rhs_ty, depth)
323                }
324                _ => false,
325            },
326
327            ty::Adt(lhs_def, lhs_args) => match rhs.kind() {
328                ty::Adt(rhs_def, rhs_args) => {
329                    lhs_def == rhs_def && self.args_may_unify_inner(lhs_args, rhs_args, depth)
330                }
331                _ => false,
332            },
333
334            // Depending on the value of const generics, we either treat generic parameters
335            // like placeholders or like inference variables.
336            ty::Param(lhs) => {
337                INSTANTIATE_LHS_WITH_INFER
338                    || match rhs.kind() {
339                        ty::Param(rhs) => lhs == rhs,
340                        _ => false,
341                    }
342            }
343
344            // Placeholder types don't unify with anything on their own.
345            ty::Placeholder(lhs) => {
346                matches!(rhs.kind(), ty::Placeholder(rhs) if lhs == rhs)
347            }
348
349            ty::Infer(var) => self.var_and_ty_may_unify(var, rhs),
350
351            // As we're walking the whole type, it may encounter projections
352            // inside of binders and what not, so we're just going to assume that
353            // projections can unify with other stuff.
354            //
355            // Looking forward to lazy normalization this is the safer strategy anyways.
356            ty::Alias(..) => true,
357
358            ty::Int(_)
359            | ty::Uint(_)
360            | ty::Float(_)
361            | ty::Str
362            | ty::Bool
363            | ty::Char
364            | ty::Never
365            | ty::Foreign(_) => lhs == rhs,
366
367            ty::Tuple(lhs) => match rhs.kind() {
368                ty::Tuple(rhs) => {
369                    lhs.len() == rhs.len()
370                        && iter::zip(lhs.iter(), rhs.iter())
371                            .all(|(lhs, rhs)| self.types_may_unify_inner(lhs, rhs, depth))
372                }
373                _ => false,
374            },
375
376            ty::Array(lhs_ty, lhs_len) => match rhs.kind() {
377                ty::Array(rhs_ty, rhs_len) => {
378                    self.types_may_unify_inner(lhs_ty, rhs_ty, depth)
379                        && self.consts_may_unify_inner(lhs_len, rhs_len)
380                }
381                _ => false,
382            },
383
384            ty::RawPtr(lhs_ty, lhs_mutbl) => match rhs.kind() {
385                ty::RawPtr(rhs_ty, rhs_mutbl) => {
386                    lhs_mutbl == rhs_mutbl && self.types_may_unify_inner(lhs_ty, rhs_ty, depth)
387                }
388                _ => false,
389            },
390
391            ty::Slice(lhs_ty) => {
392                matches!(rhs.kind(), ty::Slice(rhs_ty) if self.types_may_unify_inner(lhs_ty, rhs_ty, depth))
393            }
394
395            ty::Dynamic(lhs_preds, ..) => {
396                // Ideally we would walk the existential predicates here or at least
397                // compare their length. But considering that the relevant `Relate` impl
398                // actually sorts and deduplicates these, that doesn't work.
399                matches!(rhs.kind(), ty::Dynamic(rhs_preds, ..) if
400                    lhs_preds.principal_def_id() == rhs_preds.principal_def_id()
401                )
402            }
403
404            ty::FnPtr(lhs_sig_tys, lhs_hdr) => match rhs.kind() {
405                ty::FnPtr(rhs_sig_tys, rhs_hdr) => {
406                    let lhs_sig_tys = lhs_sig_tys.skip_binder().inputs_and_output;
407                    let rhs_sig_tys = rhs_sig_tys.skip_binder().inputs_and_output;
408
409                    lhs_hdr == rhs_hdr
410                        && lhs_sig_tys.len() == rhs_sig_tys.len()
411                        && iter::zip(lhs_sig_tys.iter(), rhs_sig_tys.iter())
412                            .all(|(lhs, rhs)| self.types_may_unify_inner(lhs, rhs, depth))
413                }
414                _ => false,
415            },
416
417            ty::Bound(..) => true,
418
419            ty::FnDef(lhs_def_id, lhs_args) => match rhs.kind() {
420                ty::FnDef(rhs_def_id, rhs_args) => {
421                    lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth)
422                }
423                _ => false,
424            },
425
426            ty::Closure(lhs_def_id, lhs_args) => match rhs.kind() {
427                ty::Closure(rhs_def_id, rhs_args) => {
428                    lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth)
429                }
430                _ => false,
431            },
432
433            ty::CoroutineClosure(lhs_def_id, lhs_args) => match rhs.kind() {
434                ty::CoroutineClosure(rhs_def_id, rhs_args) => {
435                    lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth)
436                }
437                _ => false,
438            },
439
440            ty::Coroutine(lhs_def_id, lhs_args) => match rhs.kind() {
441                ty::Coroutine(rhs_def_id, rhs_args) => {
442                    lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth)
443                }
444                _ => false,
445            },
446
447            ty::CoroutineWitness(lhs_def_id, lhs_args) => match rhs.kind() {
448                ty::CoroutineWitness(rhs_def_id, rhs_args) => {
449                    lhs_def_id == rhs_def_id && self.args_may_unify_inner(lhs_args, rhs_args, depth)
450                }
451                _ => false,
452            },
453
454            ty::Pat(lhs_ty, _) => {
455                // FIXME(pattern_types): take pattern into account
456                matches!(rhs.kind(), ty::Pat(rhs_ty, _) if self.types_may_unify_inner(lhs_ty, rhs_ty, depth))
457            }
458
459            ty::UnsafeBinder(lhs_ty) => match rhs.kind() {
460                ty::UnsafeBinder(rhs_ty) => {
461                    self.types_may_unify(lhs_ty.skip_binder(), rhs_ty.skip_binder())
462                }
463                _ => false,
464            },
465
466            ty::Error(..) => true,
467        }
468    }
469
470    // Unlike `types_may_unify_inner`, this does not take a depth as
471    // we never recurse from this function.
472    fn consts_may_unify_inner(self, lhs: I::Const, rhs: I::Const) -> bool {
473        match rhs.kind() {
474            ty::ConstKind::Param(_) => {
475                if INSTANTIATE_RHS_WITH_INFER {
476                    return true;
477                }
478            }
479
480            ty::ConstKind::Expr(_)
481            | ty::ConstKind::Unevaluated(_)
482            | ty::ConstKind::Error(_)
483            | ty::ConstKind::Infer(_)
484            | ty::ConstKind::Bound(..) => {
485                return true;
486            }
487
488            ty::ConstKind::Value(..) | ty::ConstKind::Placeholder(_) => {}
489        };
490
491        match lhs.kind() {
492            ty::ConstKind::Value(lhs_val) => match rhs.kind() {
493                ty::ConstKind::Value(rhs_val) => lhs_val.valtree() == rhs_val.valtree(),
494                _ => false,
495            },
496
497            ty::ConstKind::Param(lhs) => {
498                INSTANTIATE_LHS_WITH_INFER
499                    || match rhs.kind() {
500                        ty::ConstKind::Param(rhs) => lhs == rhs,
501                        _ => false,
502                    }
503            }
504
505            // Placeholder consts don't unify with anything on their own
506            ty::ConstKind::Placeholder(lhs) => {
507                matches!(rhs.kind(), ty::ConstKind::Placeholder(rhs) if lhs == rhs)
508            }
509
510            // As we don't necessarily eagerly evaluate constants,
511            // they might unify with any value.
512            ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => {
513                true
514            }
515
516            ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) => true,
517        }
518    }
519
520    fn var_and_ty_may_unify(self, var: ty::InferTy, ty: I::Ty) -> bool {
521        if !ty.is_known_rigid() {
522            return true;
523        }
524
525        match var {
526            ty::IntVar(_) => ty.is_integral(),
527            ty::FloatVar(_) => ty.is_floating_point(),
528            _ => true,
529        }
530    }
531}