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