diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-02-22 12:31:30 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-02-22 12:31:30 +0000 |
commit | 2cbe8a4c4be2a69b27c248ab96341c2336f983cd (patch) | |
tree | ee1a32b47f889ed132a314286cd90a07f3700216 /crates/ra_hir_ty/src/traits/builtin.rs | |
parent | 7836720f2e9a7fa01ae09ff9d51413ecd5877139 (diff) | |
parent | 5a6e770f99d1549432c1e8a1abb1aada09ad2590 (diff) |
Merge #3263
3263: Implement unsizing coercions using Chalk r=matklad a=flodiebold
These are coercions like `&[T; n] -> &[T]`, which are handled by the `Unsize` and `CoerceUnsized` traits. The impls for `Unsize` are all built in to the compiler and require special handling, so we need to provide them to Chalk.
This adds the following `Unsize` impls:
- `Unsize<[T]> for [T; _]`
- `Unsize<dyn Trait> for T where T: Trait`
- `Unsize<dyn SuperTrait> for dyn SubTrait`
Hence we are still missing the 'unsizing the last field of a generic struct' case.
Co-authored-by: Florian Diebold <[email protected]>
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir_ty/src/traits/builtin.rs')
-rw-r--r-- | crates/ra_hir_ty/src/traits/builtin.rs | 191 |
1 files changed, 189 insertions, 2 deletions
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index a537420a5..cc0f3eeb4 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs | |||
@@ -4,8 +4,12 @@ use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId}; | |||
4 | use hir_expand::name::name; | 4 | use hir_expand::name::name; |
5 | use ra_db::CrateId; | 5 | use ra_db::CrateId; |
6 | 6 | ||
7 | use super::{AssocTyValue, Impl}; | 7 | use super::{AssocTyValue, Impl, UnsizeToSuperTraitObjectData}; |
8 | use crate::{db::HirDatabase, ApplicationTy, Substs, TraitRef, Ty, TypeCtor}; | 8 | use crate::{ |
9 | db::HirDatabase, | ||
10 | utils::{all_super_traits, generics}, | ||
11 | ApplicationTy, Binders, GenericPredicate, Substs, TraitRef, Ty, TypeCtor, | ||
12 | }; | ||
9 | 13 | ||
10 | pub(super) struct BuiltinImplData { | 14 | pub(super) struct BuiltinImplData { |
11 | pub num_vars: usize, | 15 | pub num_vars: usize, |
@@ -25,6 +29,8 @@ pub(super) fn get_builtin_impls( | |||
25 | db: &impl HirDatabase, | 29 | db: &impl HirDatabase, |
26 | krate: CrateId, | 30 | krate: CrateId, |
27 | ty: &Ty, | 31 | ty: &Ty, |
32 | // The first argument for the trait, if present | ||
33 | arg: &Option<Ty>, | ||
28 | trait_: TraitId, | 34 | trait_: TraitId, |
29 | mut callback: impl FnMut(Impl), | 35 | mut callback: impl FnMut(Impl), |
30 | ) { | 36 | ) { |
@@ -43,12 +49,60 @@ pub(super) fn get_builtin_impls( | |||
43 | } | 49 | } |
44 | } | 50 | } |
45 | } | 51 | } |
52 | |||
53 | let unsize_trait = get_unsize_trait(db, krate); | ||
54 | if let Some(actual_trait) = unsize_trait { | ||
55 | if trait_ == actual_trait { | ||
56 | get_builtin_unsize_impls(db, krate, ty, arg, callback); | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | fn get_builtin_unsize_impls( | ||
62 | db: &impl HirDatabase, | ||
63 | krate: CrateId, | ||
64 | ty: &Ty, | ||
65 | // The first argument for the trait, if present | ||
66 | arg: &Option<Ty>, | ||
67 | mut callback: impl FnMut(Impl), | ||
68 | ) { | ||
69 | if !check_unsize_impl_prerequisites(db, krate) { | ||
70 | return; | ||
71 | } | ||
72 | |||
73 | if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, .. }) = ty { | ||
74 | callback(Impl::UnsizeArray); | ||
75 | return; // array is unsized, the rest of the impls shouldn't apply | ||
76 | } | ||
77 | |||
78 | if let Some(target_trait) = arg.as_ref().and_then(|t| t.dyn_trait_ref()) { | ||
79 | // FIXME what about more complicated dyn tys with marker traits? | ||
80 | if let Some(trait_ref) = ty.dyn_trait_ref() { | ||
81 | if trait_ref.trait_ != target_trait.trait_ { | ||
82 | let super_traits = all_super_traits(db, trait_ref.trait_); | ||
83 | if super_traits.contains(&target_trait.trait_) { | ||
84 | callback(Impl::UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData { | ||
85 | trait_: trait_ref.trait_, | ||
86 | super_trait: target_trait.trait_, | ||
87 | })); | ||
88 | } | ||
89 | } | ||
90 | } else { | ||
91 | // FIXME only for sized types | ||
92 | callback(Impl::UnsizeToTraitObject(target_trait.trait_)); | ||
93 | } | ||
94 | } | ||
46 | } | 95 | } |
47 | 96 | ||
48 | pub(super) fn impl_datum(db: &impl HirDatabase, krate: CrateId, impl_: Impl) -> BuiltinImplData { | 97 | pub(super) fn impl_datum(db: &impl HirDatabase, krate: CrateId, impl_: Impl) -> BuiltinImplData { |
49 | match impl_ { | 98 | match impl_ { |
50 | Impl::ImplBlock(_) => unreachable!(), | 99 | Impl::ImplBlock(_) => unreachable!(), |
51 | Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data), | 100 | Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data), |
101 | Impl::UnsizeArray => array_unsize_impl_datum(db, krate), | ||
102 | Impl::UnsizeToTraitObject(trait_) => trait_object_unsize_impl_datum(db, krate, trait_), | ||
103 | Impl::UnsizeToSuperTraitObject(data) => { | ||
104 | super_trait_object_unsize_impl_datum(db, krate, data) | ||
105 | } | ||
52 | } | 106 | } |
53 | } | 107 | } |
54 | 108 | ||
@@ -65,6 +119,8 @@ pub(super) fn associated_ty_value( | |||
65 | } | 119 | } |
66 | } | 120 | } |
67 | 121 | ||
122 | // Closure Fn trait impls | ||
123 | |||
68 | fn check_closure_fn_trait_impl_prerequisites( | 124 | fn check_closure_fn_trait_impl_prerequisites( |
69 | db: &impl HirDatabase, | 125 | db: &impl HirDatabase, |
70 | krate: CrateId, | 126 | krate: CrateId, |
@@ -165,6 +221,129 @@ fn closure_fn_trait_output_assoc_ty_value( | |||
165 | } | 221 | } |
166 | } | 222 | } |
167 | 223 | ||
224 | // Array unsizing | ||
225 | |||
226 | fn check_unsize_impl_prerequisites(db: &impl HirDatabase, krate: CrateId) -> bool { | ||
227 | // the Unsize trait needs to exist and have two type parameters (Self and T) | ||
228 | let unsize_trait = match get_unsize_trait(db, krate) { | ||
229 | Some(t) => t, | ||
230 | None => return false, | ||
231 | }; | ||
232 | let generic_params = generics(db, unsize_trait.into()); | ||
233 | generic_params.len() == 2 | ||
234 | } | ||
235 | |||
236 | fn array_unsize_impl_datum(db: &impl HirDatabase, krate: CrateId) -> BuiltinImplData { | ||
237 | // impl<T> Unsize<[T]> for [T; _] | ||
238 | // (this can be a single impl because we don't distinguish array sizes currently) | ||
239 | |||
240 | let trait_ = get_unsize_trait(db, krate) // get unsize trait | ||
241 | // the existence of the Unsize trait has been checked before | ||
242 | .expect("Unsize trait missing"); | ||
243 | |||
244 | let var = Ty::Bound(0); | ||
245 | let substs = Substs::builder(2) | ||
246 | .push(Ty::apply_one(TypeCtor::Array, var.clone())) | ||
247 | .push(Ty::apply_one(TypeCtor::Slice, var)) | ||
248 | .build(); | ||
249 | |||
250 | let trait_ref = TraitRef { trait_, substs }; | ||
251 | |||
252 | BuiltinImplData { | ||
253 | num_vars: 1, | ||
254 | trait_ref, | ||
255 | where_clauses: Vec::new(), | ||
256 | assoc_ty_values: Vec::new(), | ||
257 | } | ||
258 | } | ||
259 | |||
260 | // Trait object unsizing | ||
261 | |||
262 | fn trait_object_unsize_impl_datum( | ||
263 | db: &impl HirDatabase, | ||
264 | krate: CrateId, | ||
265 | trait_: TraitId, | ||
266 | ) -> BuiltinImplData { | ||
267 | // impl<T, T1, ...> Unsize<dyn Trait<T1, ...>> for T where T: Trait<T1, ...> | ||
268 | |||
269 | let unsize_trait = get_unsize_trait(db, krate) // get unsize trait | ||
270 | // the existence of the Unsize trait has been checked before | ||
271 | .expect("Unsize trait missing"); | ||
272 | |||
273 | let self_ty = Ty::Bound(0); | ||
274 | |||
275 | let target_substs = Substs::build_for_def(db, trait_) | ||
276 | .push(Ty::Bound(0)) | ||
277 | // starting from ^2 because we want to start with ^1 outside of the | ||
278 | // `dyn`, which is ^2 inside | ||
279 | .fill_with_bound_vars(2) | ||
280 | .build(); | ||
281 | let num_vars = target_substs.len(); | ||
282 | let target_trait_ref = TraitRef { trait_, substs: target_substs }; | ||
283 | let target_bounds = vec![GenericPredicate::Implemented(target_trait_ref)]; | ||
284 | |||
285 | let self_substs = Substs::build_for_def(db, trait_).fill_with_bound_vars(0).build(); | ||
286 | let self_trait_ref = TraitRef { trait_, substs: self_substs }; | ||
287 | let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)]; | ||
288 | |||
289 | let impl_substs = | ||
290 | Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.clone().into())).build(); | ||
291 | |||
292 | let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs }; | ||
293 | |||
294 | BuiltinImplData { num_vars, trait_ref, where_clauses, assoc_ty_values: Vec::new() } | ||
295 | } | ||
296 | |||
297 | fn super_trait_object_unsize_impl_datum( | ||
298 | db: &impl HirDatabase, | ||
299 | krate: CrateId, | ||
300 | data: UnsizeToSuperTraitObjectData, | ||
301 | ) -> BuiltinImplData { | ||
302 | // impl<T1, ...> Unsize<dyn SuperTrait> for dyn Trait<T1, ...> | ||
303 | |||
304 | let unsize_trait = get_unsize_trait(db, krate) // get unsize trait | ||
305 | // the existence of the Unsize trait has been checked before | ||
306 | .expect("Unsize trait missing"); | ||
307 | |||
308 | let self_substs = Substs::build_for_def(db, data.trait_).fill_with_bound_vars(0).build(); | ||
309 | |||
310 | let num_vars = self_substs.len() - 1; | ||
311 | |||
312 | let self_trait_ref = TraitRef { trait_: data.trait_, substs: self_substs.clone() }; | ||
313 | let self_bounds = vec![GenericPredicate::Implemented(self_trait_ref.clone())]; | ||
314 | |||
315 | // we need to go from our trait to the super trait, substituting type parameters | ||
316 | let path = crate::utils::find_super_trait_path(db, data.trait_, data.super_trait); | ||
317 | |||
318 | let mut current_trait_ref = self_trait_ref; | ||
319 | for t in path.into_iter().skip(1) { | ||
320 | let bounds = db.generic_predicates(current_trait_ref.trait_.into()); | ||
321 | let super_trait_ref = bounds | ||
322 | .iter() | ||
323 | .find_map(|b| match &b.value { | ||
324 | GenericPredicate::Implemented(tr) | ||
325 | if tr.trait_ == t && tr.substs[0] == Ty::Bound(0) => | ||
326 | { | ||
327 | Some(Binders { value: tr, num_binders: b.num_binders }) | ||
328 | } | ||
329 | _ => None, | ||
330 | }) | ||
331 | .expect("trait bound for known super trait not found"); | ||
332 | current_trait_ref = super_trait_ref.cloned().subst(¤t_trait_ref.substs); | ||
333 | } | ||
334 | |||
335 | let super_bounds = vec![GenericPredicate::Implemented(current_trait_ref)]; | ||
336 | |||
337 | let substs = Substs::builder(2) | ||
338 | .push(Ty::Dyn(self_bounds.into())) | ||
339 | .push(Ty::Dyn(super_bounds.into())) | ||
340 | .build(); | ||
341 | |||
342 | let trait_ref = TraitRef { trait_: unsize_trait, substs }; | ||
343 | |||
344 | BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() } | ||
345 | } | ||
346 | |||
168 | fn get_fn_trait( | 347 | fn get_fn_trait( |
169 | db: &impl HirDatabase, | 348 | db: &impl HirDatabase, |
170 | krate: CrateId, | 349 | krate: CrateId, |
@@ -176,3 +355,11 @@ fn get_fn_trait( | |||
176 | _ => None, | 355 | _ => None, |
177 | } | 356 | } |
178 | } | 357 | } |
358 | |||
359 | fn get_unsize_trait(db: &impl HirDatabase, krate: CrateId) -> Option<TraitId> { | ||
360 | let target = db.lang_item(krate, "unsize".into())?; | ||
361 | match target { | ||
362 | LangItemTarget::TraitId(t) => Some(t), | ||
363 | _ => None, | ||
364 | } | ||
365 | } | ||