diff options
Diffstat (limited to 'crates/ra_hir_ty/src/infer/coerce.rs')
-rw-r--r-- | crates/ra_hir_ty/src/infer/coerce.rs | 197 |
1 files changed, 0 insertions, 197 deletions
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs deleted file mode 100644 index 32c7c57cd..000000000 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ /dev/null | |||
@@ -1,197 +0,0 @@ | |||
1 | //! Coercion logic. Coercions are certain type conversions that can implicitly | ||
2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions | ||
3 | //! like going from `&Vec<T>` to `&[T]`. | ||
4 | //! | ||
5 | //! See: https://doc.rust-lang.org/nomicon/coercions.html | ||
6 | |||
7 | use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; | ||
8 | use test_utils::mark; | ||
9 | |||
10 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; | ||
11 | |||
12 | use super::{unify::TypeVarValue, InEnvironment, InferTy, InferenceContext}; | ||
13 | |||
14 | impl<'a> InferenceContext<'a> { | ||
15 | /// Unify two types, but may coerce the first one to the second one | ||
16 | /// using "implicit coercion rules" if needed. | ||
17 | pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { | ||
18 | let from_ty = self.resolve_ty_shallow(from_ty).into_owned(); | ||
19 | let to_ty = self.resolve_ty_shallow(to_ty); | ||
20 | self.coerce_inner(from_ty, &to_ty) | ||
21 | } | ||
22 | |||
23 | /// Merge two types from different branches, with possible coercion. | ||
24 | /// | ||
25 | /// Mostly this means trying to coerce one to the other, but | ||
26 | /// - if we have two function types for different functions, we need to | ||
27 | /// coerce both to function pointers; | ||
28 | /// - if we were concerned with lifetime subtyping, we'd need to look for a | ||
29 | /// least upper bound. | ||
30 | pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { | ||
31 | if self.coerce(ty1, ty2) { | ||
32 | ty2.clone() | ||
33 | } else if self.coerce(ty2, ty1) { | ||
34 | ty1.clone() | ||
35 | } else { | ||
36 | if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { | ||
37 | mark::hit!(coerce_fn_reification); | ||
38 | // Special case: two function types. Try to coerce both to | ||
39 | // pointers to have a chance at getting a match. See | ||
40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | ||
41 | let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); | ||
42 | let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); | ||
43 | let ptr_ty1 = Ty::fn_ptr(sig1); | ||
44 | let ptr_ty2 = Ty::fn_ptr(sig2); | ||
45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | ||
46 | } else { | ||
47 | mark::hit!(coerce_merge_fail_fallback); | ||
48 | ty1.clone() | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool { | ||
54 | match (&from_ty, to_ty) { | ||
55 | // Never type will make type variable to fallback to Never Type instead of Unknown. | ||
56 | (ty_app!(TypeCtor::Never), Ty::Infer(InferTy::TypeVar(tv))) => { | ||
57 | let var = self.table.new_maybe_never_type_var(); | ||
58 | self.table.var_unification_table.union_value(*tv, TypeVarValue::Known(var)); | ||
59 | return true; | ||
60 | } | ||
61 | (ty_app!(TypeCtor::Never), _) => return true, | ||
62 | |||
63 | // Trivial cases, this should go after `never` check to | ||
64 | // avoid infer result type to be never | ||
65 | _ => { | ||
66 | if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) { | ||
67 | return true; | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | // Pointer weakening and function to pointer | ||
73 | match (&mut from_ty, to_ty) { | ||
74 | // `*mut T`, `&mut T, `&T`` -> `*const T` | ||
75 | // `&mut T` -> `&T` | ||
76 | // `&mut T` -> `*mut T` | ||
77 | (ty_app!(c1@TypeCtor::RawPtr(_)), ty_app!(c2@TypeCtor::RawPtr(Mutability::Shared))) | ||
78 | | (ty_app!(c1@TypeCtor::Ref(_)), ty_app!(c2@TypeCtor::RawPtr(Mutability::Shared))) | ||
79 | | (ty_app!(c1@TypeCtor::Ref(_)), ty_app!(c2@TypeCtor::Ref(Mutability::Shared))) | ||
80 | | (ty_app!(c1@TypeCtor::Ref(Mutability::Mut)), ty_app!(c2@TypeCtor::RawPtr(_))) => { | ||
81 | *c1 = *c2; | ||
82 | } | ||
83 | |||
84 | // Illegal mutablity conversion | ||
85 | ( | ||
86 | ty_app!(TypeCtor::RawPtr(Mutability::Shared)), | ||
87 | ty_app!(TypeCtor::RawPtr(Mutability::Mut)), | ||
88 | ) | ||
89 | | ( | ||
90 | ty_app!(TypeCtor::Ref(Mutability::Shared)), | ||
91 | ty_app!(TypeCtor::Ref(Mutability::Mut)), | ||
92 | ) => return false, | ||
93 | |||
94 | // `{function_type}` -> `fn()` | ||
95 | (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => { | ||
96 | match from_ty.callable_sig(self.db) { | ||
97 | None => return false, | ||
98 | Some(sig) => { | ||
99 | from_ty = Ty::fn_ptr(sig); | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | (ty_app!(TypeCtor::Closure { .. }, params), ty_app!(TypeCtor::FnPtr { .. })) => { | ||
105 | from_ty = params[0].clone(); | ||
106 | } | ||
107 | |||
108 | _ => {} | ||
109 | } | ||
110 | |||
111 | if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { | ||
112 | return ret; | ||
113 | } | ||
114 | |||
115 | // Auto Deref if cannot coerce | ||
116 | match (&from_ty, to_ty) { | ||
117 | // FIXME: DerefMut | ||
118 | (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) => { | ||
119 | self.unify_autoderef_behind_ref(&st1[0], &st2[0]) | ||
120 | } | ||
121 | |||
122 | // Otherwise, normal unify | ||
123 | _ => self.unify(&from_ty, to_ty), | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` | ||
128 | /// | ||
129 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html | ||
130 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> { | ||
131 | let krate = self.resolver.krate().unwrap(); | ||
132 | let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) { | ||
133 | Some(LangItemTarget::TraitId(trait_)) => trait_, | ||
134 | _ => return None, | ||
135 | }; | ||
136 | |||
137 | let generic_params = crate::utils::generics(self.db.upcast(), coerce_unsized_trait.into()); | ||
138 | if generic_params.len() != 2 { | ||
139 | // The CoerceUnsized trait should have two generic params: Self and T. | ||
140 | return None; | ||
141 | } | ||
142 | |||
143 | let substs = Substs::build_for_generics(&generic_params) | ||
144 | .push(from_ty.clone()) | ||
145 | .push(to_ty.clone()) | ||
146 | .build(); | ||
147 | let trait_ref = TraitRef { trait_: coerce_unsized_trait, substs }; | ||
148 | let goal = InEnvironment::new(self.trait_env.clone(), Obligation::Trait(trait_ref)); | ||
149 | |||
150 | let canonicalizer = self.canonicalizer(); | ||
151 | let canonicalized = canonicalizer.canonicalize_obligation(goal); | ||
152 | |||
153 | let solution = self.db.trait_solve(krate, canonicalized.value.clone())?; | ||
154 | |||
155 | match solution { | ||
156 | Solution::Unique(v) => { | ||
157 | canonicalized.apply_solution(self, v.0); | ||
158 | } | ||
159 | _ => return None, | ||
160 | }; | ||
161 | |||
162 | Some(true) | ||
163 | } | ||
164 | |||
165 | /// Unify `from_ty` to `to_ty` with optional auto Deref | ||
166 | /// | ||
167 | /// Note that the parameters are already stripped the outer reference. | ||
168 | fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { | ||
169 | let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone()); | ||
170 | let to_ty = self.resolve_ty_shallow(&to_ty); | ||
171 | // FIXME: Auto DerefMut | ||
172 | for derefed_ty in autoderef::autoderef( | ||
173 | self.db, | ||
174 | self.resolver.krate(), | ||
175 | InEnvironment { | ||
176 | value: canonicalized.value.clone(), | ||
177 | environment: self.trait_env.clone(), | ||
178 | }, | ||
179 | ) { | ||
180 | let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value); | ||
181 | match (&*self.resolve_ty_shallow(&derefed_ty), &*to_ty) { | ||
182 | // Stop when constructor matches. | ||
183 | (ty_app!(from_ctor, st1), ty_app!(to_ctor, st2)) if from_ctor == to_ctor => { | ||
184 | // It will not recurse to `coerce`. | ||
185 | return self.table.unify_substs(st1, st2, 0); | ||
186 | } | ||
187 | _ => { | ||
188 | if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) { | ||
189 | return true; | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | false | ||
196 | } | ||
197 | } | ||