hir_ty/
chalk_ext.rs

1//! Various extensions traits for Chalk types.
2
3use chalk_ir::{
4    FloatTy, IntTy, Mutability, Scalar, TyVariableKind, TypeOutlives, UintTy, cast::Cast,
5};
6use hir_def::{
7    DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
8    builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
9    hir::generics::{TypeOrConstParamData, TypeParamProvenance},
10    lang_item::LangItem,
11    type_ref::Rawness,
12};
13
14use crate::{
15    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
16    ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
17    QuantifiedWhereClause, Substitution, ToChalk, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
18    WhereClause, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
19    from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst,
20};
21
22pub trait TyExt {
23    fn is_unit(&self) -> bool;
24    fn is_integral(&self) -> bool;
25    fn is_scalar(&self) -> bool;
26    fn is_floating_point(&self) -> bool;
27    fn is_never(&self) -> bool;
28    fn is_str(&self) -> bool;
29    fn is_unknown(&self) -> bool;
30    fn contains_unknown(&self) -> bool;
31    fn is_ty_var(&self) -> bool;
32    fn is_union(&self) -> bool;
33
34    fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
35    fn as_builtin(&self) -> Option<BuiltinType>;
36    fn as_tuple(&self) -> Option<&Substitution>;
37    fn as_closure(&self) -> Option<ClosureId>;
38    fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
39    fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
40    fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>;
41    fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
42    fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
43
44    fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>;
45    fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
46
47    fn strip_references(&self) -> &Ty;
48    fn strip_reference(&self) -> &Ty;
49
50    /// If this is a `dyn Trait`, returns that trait.
51    fn dyn_trait(&self) -> Option<TraitId>;
52
53    fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
54    fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
55    fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool;
56
57    /// FIXME: Get rid of this, it's not a good abstraction
58    fn equals_ctor(&self, other: &Ty) -> bool;
59}
60
61impl TyExt for Ty {
62    fn is_unit(&self) -> bool {
63        matches!(self.kind(Interner), TyKind::Tuple(0, _))
64    }
65
66    fn is_integral(&self) -> bool {
67        matches!(
68            self.kind(Interner),
69            TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))
70                | TyKind::InferenceVar(_, TyVariableKind::Integer)
71        )
72    }
73
74    fn is_scalar(&self) -> bool {
75        matches!(self.kind(Interner), TyKind::Scalar(_))
76    }
77
78    fn is_floating_point(&self) -> bool {
79        matches!(
80            self.kind(Interner),
81            TyKind::Scalar(Scalar::Float(_)) | TyKind::InferenceVar(_, TyVariableKind::Float)
82        )
83    }
84
85    fn is_never(&self) -> bool {
86        matches!(self.kind(Interner), TyKind::Never)
87    }
88
89    fn is_str(&self) -> bool {
90        matches!(self.kind(Interner), TyKind::Str)
91    }
92
93    fn is_unknown(&self) -> bool {
94        matches!(self.kind(Interner), TyKind::Error)
95    }
96
97    fn contains_unknown(&self) -> bool {
98        self.data(Interner).flags.contains(TypeFlags::HAS_ERROR)
99    }
100
101    fn is_ty_var(&self) -> bool {
102        matches!(self.kind(Interner), TyKind::InferenceVar(_, _))
103    }
104
105    fn is_union(&self) -> bool {
106        matches!(self.adt_id(Interner), Some(AdtId(hir_def::AdtId::UnionId(_))))
107    }
108
109    fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
110        match self.kind(Interner) {
111            TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
112            _ => None,
113        }
114    }
115
116    fn as_builtin(&self) -> Option<BuiltinType> {
117        match self.kind(Interner) {
118            TyKind::Str => Some(BuiltinType::Str),
119            TyKind::Scalar(Scalar::Bool) => Some(BuiltinType::Bool),
120            TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char),
121            TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty {
122                FloatTy::F128 => BuiltinFloat::F128,
123                FloatTy::F64 => BuiltinFloat::F64,
124                FloatTy::F32 => BuiltinFloat::F32,
125                FloatTy::F16 => BuiltinFloat::F16,
126            })),
127            TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity {
128                IntTy::Isize => BuiltinInt::Isize,
129                IntTy::I8 => BuiltinInt::I8,
130                IntTy::I16 => BuiltinInt::I16,
131                IntTy::I32 => BuiltinInt::I32,
132                IntTy::I64 => BuiltinInt::I64,
133                IntTy::I128 => BuiltinInt::I128,
134            })),
135            TyKind::Scalar(Scalar::Uint(ity)) => Some(BuiltinType::Uint(match ity {
136                UintTy::Usize => BuiltinUint::Usize,
137                UintTy::U8 => BuiltinUint::U8,
138                UintTy::U16 => BuiltinUint::U16,
139                UintTy::U32 => BuiltinUint::U32,
140                UintTy::U64 => BuiltinUint::U64,
141                UintTy::U128 => BuiltinUint::U128,
142            })),
143            _ => None,
144        }
145    }
146
147    fn as_tuple(&self) -> Option<&Substitution> {
148        match self.kind(Interner) {
149            TyKind::Tuple(_, substs) => Some(substs),
150            _ => None,
151        }
152    }
153
154    fn as_closure(&self) -> Option<ClosureId> {
155        match self.kind(Interner) {
156            TyKind::Closure(id, _) => Some(*id),
157            _ => None,
158        }
159    }
160
161    fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
162        match self.callable_def(db) {
163            Some(CallableDefId::FunctionId(func)) => Some(func),
164            Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None,
165        }
166    }
167
168    fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
169        match self.kind(Interner) {
170            TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)),
171            _ => None,
172        }
173    }
174
175    fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)> {
176        match self.kind(Interner) {
177            TyKind::Raw(mutability, ty) => Some((ty, *mutability)),
178            _ => None,
179        }
180    }
181
182    fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
183        match self.kind(Interner) {
184            TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
185            TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)),
186            _ => None,
187        }
188    }
189
190    fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
191        match *self.kind(Interner) {
192            TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
193            TyKind::FnDef(callable, ..) => {
194                Some(GenericDefId::from_callable(db, ToChalk::from_chalk(db, callable)))
195            }
196            TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
197            TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
198            _ => None,
199        }
200    }
201
202    fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
203        match self.kind(Interner) {
204            &TyKind::FnDef(def, ..) => Some(ToChalk::from_chalk(db, def)),
205            _ => None,
206        }
207    }
208
209    fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
210        match self.kind(Interner) {
211            TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
212            TyKind::FnDef(def, parameters) => Some(CallableSig::from_def(db, *def, parameters)),
213            TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
214            _ => None,
215        }
216    }
217
218    fn dyn_trait(&self) -> Option<TraitId> {
219        let trait_ref = match self.kind(Interner) {
220            // The principal trait bound should be the first element of the bounds. This is an
221            // invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
222            // FIXME: dyn types may not have principal trait and we don't want to return auto trait
223            // here.
224            TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().first().and_then(|b| {
225                match b.skip_binders() {
226                    WhereClause::Implemented(trait_ref) => Some(trait_ref),
227                    _ => None,
228                }
229            }),
230            _ => None,
231        }?;
232        Some(from_chalk_trait_id(trait_ref.trait_id))
233    }
234
235    fn strip_references(&self) -> &Ty {
236        let mut t: &Ty = self;
237        while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(Interner) {
238            t = ty;
239        }
240        t
241    }
242
243    fn strip_reference(&self) -> &Ty {
244        self.as_reference().map_or(self, |(ty, _, _)| ty)
245    }
246
247    fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
248        match self.kind(Interner) {
249            TyKind::OpaqueType(opaque_ty_id, subst) => {
250                match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
251                    ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
252                        let krate = def.module(db).krate();
253                        if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) {
254                            // This is only used by type walking.
255                            // Parameters will be walked outside, and projection predicate is not used.
256                            // So just provide the Future trait.
257                            let impl_bound = Binders::empty(
258                                Interner,
259                                WhereClause::Implemented(TraitRef {
260                                    trait_id: to_chalk_trait_id(future_trait),
261                                    substitution: Substitution::empty(Interner),
262                                }),
263                            );
264                            Some(vec![impl_bound])
265                        } else {
266                            None
267                        }
268                    }
269                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
270                        db.return_type_impl_traits(func).map(|it| {
271                            let data =
272                                (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
273                            data.substitute(Interner, &subst).into_value_and_skipped_binders().0
274                        })
275                    }
276                    ImplTraitId::TypeAliasImplTrait(alias, idx) => {
277                        db.type_alias_impl_traits(alias).map(|it| {
278                            let data =
279                                (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
280                            data.substitute(Interner, &subst).into_value_and_skipped_binders().0
281                        })
282                    }
283                }
284            }
285            TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
286                let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into())
287                {
288                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
289                        db.return_type_impl_traits(func).map(|it| {
290                            let data =
291                                (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
292                            data.substitute(Interner, &opaque_ty.substitution)
293                        })
294                    }
295                    ImplTraitId::TypeAliasImplTrait(alias, idx) => {
296                        db.type_alias_impl_traits(alias).map(|it| {
297                            let data =
298                                (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
299                            data.substitute(Interner, &opaque_ty.substitution)
300                        })
301                    }
302                    // It always has an parameter for Future::Output type.
303                    ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
304                };
305
306                predicates.map(|it| it.into_value_and_skipped_binders().0)
307            }
308            TyKind::Placeholder(idx) => {
309                let id = from_placeholder_idx(db, *idx).0;
310                let generic_params = db.generic_params(id.parent);
311                let param_data = &generic_params[id.local_id];
312                match param_data {
313                    TypeOrConstParamData::TypeParamData(p) => match p.provenance {
314                        TypeParamProvenance::ArgumentImplTrait => {
315                            let substs = TyBuilder::placeholder_subst(db, id.parent);
316                            let predicates = db
317                                .generic_predicates(id.parent)
318                                .iter()
319                                .map(|pred| pred.clone().substitute(Interner, &substs))
320                                .filter(|wc| match wc.skip_binders() {
321                                    WhereClause::Implemented(tr) => {
322                                        &tr.self_type_parameter(Interner) == self
323                                    }
324                                    WhereClause::AliasEq(AliasEq {
325                                        alias: AliasTy::Projection(proj),
326                                        ty: _,
327                                    }) => &proj.self_type_parameter(db) == self,
328                                    WhereClause::TypeOutlives(TypeOutlives { ty, lifetime: _ }) => {
329                                        ty == self
330                                    }
331                                    _ => false,
332                                })
333                                .collect::<Vec<_>>();
334
335                            Some(predicates)
336                        }
337                        _ => None,
338                    },
339                    _ => None,
340                }
341            }
342            _ => None,
343        }
344    }
345
346    fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
347        match self.kind(Interner) {
348            TyKind::AssociatedType(id, ..) => match from_assoc_type_id(*id).lookup(db).container {
349                ItemContainerId::TraitId(trait_id) => Some(trait_id),
350                _ => None,
351            },
352            TyKind::Alias(AliasTy::Projection(projection_ty)) => {
353                match from_assoc_type_id(projection_ty.associated_ty_id).lookup(db).container {
354                    ItemContainerId::TraitId(trait_id) => Some(trait_id),
355                    _ => None,
356                }
357            }
358            _ => None,
359        }
360    }
361
362    fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
363        let crate_id = owner.module(db).krate();
364        let Some(copy_trait) = LangItem::Copy.resolve_trait(db, crate_id) else {
365            return false;
366        };
367        let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
368        let env = db.trait_environment_for_body(owner);
369        let goal = Canonical {
370            value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
371            binders: CanonicalVarKinds::empty(Interner),
372        };
373        !db.trait_solve(crate_id, None, goal).no_solution()
374    }
375
376    fn equals_ctor(&self, other: &Ty) -> bool {
377        match (self.kind(Interner), other.kind(Interner)) {
378            (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
379            (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_, _), TyKind::Array(_, _)) => {
380                true
381            }
382            (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2,
383            (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2,
384            (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => {
385                ty_id == ty_id2
386            }
387            (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2,
388            (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2,
389            (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..))
390            | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => {
391                mutability == mutability2
392            }
393            (
394                TyKind::Function(FnPointer { num_binders, sig, .. }),
395                TyKind::Function(FnPointer { num_binders: num_binders2, sig: sig2, .. }),
396            ) => num_binders == num_binders2 && sig == sig2,
397            (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => {
398                cardinality == cardinality2
399            }
400            (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true,
401            (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2,
402            _ => false,
403        }
404    }
405}
406
407pub trait ProjectionTyExt {
408    fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
409    fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
410    fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty;
411}
412
413impl ProjectionTyExt for ProjectionTy {
414    fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
415        // FIXME: something like `Split` trait from chalk-solve might be nice.
416        let generics = generics(db, from_assoc_type_id(self.associated_ty_id).into());
417        let parent_len = generics.parent_generics().map_or(0, |g| g.len_self());
418        let substitution =
419            Substitution::from_iter(Interner, self.substitution.iter(Interner).take(parent_len));
420        TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution }
421    }
422
423    fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
424        match from_assoc_type_id(self.associated_ty_id).lookup(db).container {
425            ItemContainerId::TraitId(it) => it,
426            _ => panic!("projection ty without parent trait"),
427        }
428    }
429
430    fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty {
431        self.trait_ref(db).self_type_parameter(Interner)
432    }
433}
434
435pub trait DynTyExt {
436    fn principal(&self) -> Option<Binders<Binders<&TraitRef>>>;
437    fn principal_id(&self) -> Option<chalk_ir::TraitId<Interner>>;
438}
439
440impl DynTyExt for DynTy {
441    fn principal(&self) -> Option<Binders<Binders<&TraitRef>>> {
442        self.bounds.as_ref().filter_map(|bounds| {
443            bounds.interned().first().and_then(|b| {
444                b.as_ref().filter_map(|b| match b {
445                    crate::WhereClause::Implemented(trait_ref) => Some(trait_ref),
446                    _ => None,
447                })
448            })
449        })
450    }
451
452    fn principal_id(&self) -> Option<chalk_ir::TraitId<Interner>> {
453        self.bounds.skip_binders().interned().first().and_then(|b| match b.skip_binders() {
454            crate::WhereClause::Implemented(trait_ref) => Some(trait_ref.trait_id),
455            _ => None,
456        })
457    }
458}
459
460pub trait TraitRefExt {
461    fn hir_trait_id(&self) -> TraitId;
462}
463
464impl TraitRefExt for TraitRef {
465    fn hir_trait_id(&self) -> TraitId {
466        from_chalk_trait_id(self.trait_id)
467    }
468}