diff options
author | Florian Diebold <[email protected]> | 2021-05-01 20:53:10 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2021-05-21 16:48:33 +0100 |
commit | 693582946fae1813627ad59f60a31c9237e98744 (patch) | |
tree | 7c546542a5e76b8e1a958f867faddd1c04b05d07 /crates | |
parent | 84074cb1852aa702e1307e9533e1fa3448e3e04f (diff) |
Rewrite coercion using the new unification
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_ty/src/builder.rs | 10 | ||||
-rw-r--r-- | crates/hir_ty/src/chalk_ext.rs | 5 | ||||
-rw-r--r-- | crates/hir_ty/src/infer.rs | 12 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/coerce.rs | 401 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/unify.rs | 37 | ||||
-rw-r--r-- | crates/hir_ty/src/lib.rs | 11 |
6 files changed, 371 insertions, 105 deletions
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs index e25ef866d..49d069541 100644 --- a/crates/hir_ty/src/builder.rs +++ b/crates/hir_ty/src/builder.rs | |||
@@ -77,15 +77,7 @@ impl TyBuilder<()> { | |||
77 | } | 77 | } |
78 | 78 | ||
79 | pub fn fn_ptr(sig: CallableSig) -> Ty { | 79 | pub fn fn_ptr(sig: CallableSig) -> Ty { |
80 | TyKind::Function(FnPointer { | 80 | TyKind::Function(sig.to_fn_ptr()).intern(&Interner) |
81 | num_binders: 0, | ||
82 | sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs }, | ||
83 | substitution: FnSubst(Substitution::from_iter( | ||
84 | &Interner, | ||
85 | sig.params_and_return.iter().cloned(), | ||
86 | )), | ||
87 | }) | ||
88 | .intern(&Interner) | ||
89 | } | 81 | } |
90 | 82 | ||
91 | pub fn builtin(builtin: BuiltinType) -> Ty { | 83 | pub fn builtin(builtin: BuiltinType) -> Ty { |
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs index 5232a7d80..df340a6ca 100644 --- a/crates/hir_ty/src/chalk_ext.rs +++ b/crates/hir_ty/src/chalk_ext.rs | |||
@@ -18,6 +18,7 @@ pub trait TyExt { | |||
18 | fn is_unit(&self) -> bool; | 18 | fn is_unit(&self) -> bool; |
19 | fn is_never(&self) -> bool; | 19 | fn is_never(&self) -> bool; |
20 | fn is_unknown(&self) -> bool; | 20 | fn is_unknown(&self) -> bool; |
21 | fn is_ty_var(&self) -> bool; | ||
21 | 22 | ||
22 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; | 23 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; |
23 | fn as_builtin(&self) -> Option<BuiltinType>; | 24 | fn as_builtin(&self) -> Option<BuiltinType>; |
@@ -55,6 +56,10 @@ impl TyExt for Ty { | |||
55 | matches!(self.kind(&Interner), TyKind::Error) | 56 | matches!(self.kind(&Interner), TyKind::Error) |
56 | } | 57 | } |
57 | 58 | ||
59 | fn is_ty_var(&self) -> bool { | ||
60 | matches!(self.kind(&Interner), TyKind::InferenceVar(_, _)) | ||
61 | } | ||
62 | |||
58 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { | 63 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { |
59 | match self.kind(&Interner) { | 64 | match self.kind(&Interner) { |
60 | TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), | 65 | TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), |
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 2a82f1b47..603068ab5 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -106,6 +106,14 @@ impl Default for BindingMode { | |||
106 | } | 106 | } |
107 | } | 107 | } |
108 | 108 | ||
109 | #[derive(Debug)] | ||
110 | pub(crate) struct InferOk { | ||
111 | // obligations | ||
112 | } | ||
113 | #[derive(Debug)] | ||
114 | pub(crate) struct TypeError; | ||
115 | pub(crate) type InferResult = Result<InferOk, TypeError>; | ||
116 | |||
109 | /// A mismatch between an expected and an inferred type. | 117 | /// A mismatch between an expected and an inferred type. |
110 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 118 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
111 | pub struct TypeMismatch { | 119 | pub struct TypeMismatch { |
@@ -390,6 +398,10 @@ impl<'a> InferenceContext<'a> { | |||
390 | self.table.unify(ty1, ty2) | 398 | self.table.unify(ty1, ty2) |
391 | } | 399 | } |
392 | 400 | ||
401 | fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty) -> InferResult { | ||
402 | self.table.unify_inner(ty1, ty2) | ||
403 | } | ||
404 | |||
393 | // FIXME get rid of this, instead resolve shallowly where necessary | 405 | // FIXME get rid of this, instead resolve shallowly where necessary |
394 | /// Resolves the type as far as currently possible, replacing type variables | 406 | /// Resolves the type as far as currently possible, replacing type variables |
395 | /// by their known types. All types returned by the infer_* functions should | 407 | /// by their known types. All types returned by the infer_* functions should |
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index ae858b1b0..86a7cd4c2 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs | |||
@@ -2,14 +2,18 @@ | |||
2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions | 2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions |
3 | //! like going from `&Vec<T>` to `&[T]`. | 3 | //! like going from `&Vec<T>` to `&[T]`. |
4 | //! | 4 | //! |
5 | //! See: https://doc.rust-lang.org/nomicon/coercions.html | 5 | //! See https://doc.rust-lang.org/nomicon/coercions.html and |
6 | //! librustc_typeck/check/coercion.rs. | ||
6 | 7 | ||
7 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; | 8 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; |
8 | use hir_def::lang_item::LangItemTarget; | 9 | use hir_def::lang_item::LangItemTarget; |
9 | 10 | ||
10 | use crate::{autoderef, Canonical, DomainGoal, Interner, Solution, Ty, TyBuilder, TyExt, TyKind}; | 11 | use crate::{ |
12 | autoderef, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Interner, Solution, | ||
13 | Substitution, Ty, TyBuilder, TyExt, TyKind, | ||
14 | }; | ||
11 | 15 | ||
12 | use super::{InEnvironment, InferenceContext}; | 16 | use super::{InEnvironment, InferOk, InferResult, InferenceContext, TypeError}; |
13 | 17 | ||
14 | impl<'a> InferenceContext<'a> { | 18 | impl<'a> InferenceContext<'a> { |
15 | /// Unify two types, but may coerce the first one to the second one | 19 | /// Unify two types, but may coerce the first one to the second one |
@@ -17,7 +21,16 @@ impl<'a> InferenceContext<'a> { | |||
17 | pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { | 21 | 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(); | 22 | let from_ty = self.resolve_ty_shallow(from_ty).into_owned(); |
19 | let to_ty = self.resolve_ty_shallow(to_ty); | 23 | let to_ty = self.resolve_ty_shallow(to_ty); |
20 | self.coerce_inner(from_ty, &to_ty) | 24 | match self.coerce_inner(from_ty, &to_ty) { |
25 | Ok(_result) => { | ||
26 | // TODO deal with goals | ||
27 | true | ||
28 | } | ||
29 | Err(_) => { | ||
30 | // FIXME deal with error | ||
31 | false | ||
32 | } | ||
33 | } | ||
21 | } | 34 | } |
22 | 35 | ||
23 | /// Merge two types from different branches, with possible coercion. | 36 | /// Merge two types from different branches, with possible coercion. |
@@ -52,93 +65,308 @@ impl<'a> InferenceContext<'a> { | |||
52 | } | 65 | } |
53 | } | 66 | } |
54 | 67 | ||
55 | fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool { | 68 | fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> InferResult { |
56 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | 69 | if from_ty.is_never() { |
57 | // Never type will make type variable to fallback to Never Type instead of Unknown. | 70 | // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound |
58 | (TyKind::Never, TyKind::InferenceVar(tv, TyVariableKind::General)) => { | 71 | // type variable, we want `?T` to fallback to `!` if not |
59 | self.table.type_variable_table.set_diverging(*tv, true); | 72 | // otherwise constrained. An example where this arises: |
60 | return true; | 73 | // |
74 | // let _: Option<?T> = Some({ return; }); | ||
75 | // | ||
76 | // here, we would coerce from `!` to `?T`. | ||
77 | match to_ty.kind(&Interner) { | ||
78 | TyKind::InferenceVar(tv, TyVariableKind::General) => { | ||
79 | self.table.type_variable_table.set_diverging(*tv, true); | ||
80 | } | ||
81 | _ => {} | ||
61 | } | 82 | } |
62 | (TyKind::Never, _) => return true, | 83 | return Ok(InferOk {}); |
84 | } | ||
63 | 85 | ||
64 | // Trivial cases, this should go after `never` check to | 86 | // Consider coercing the subtype to a DST |
65 | // avoid infer result type to be never | 87 | if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { |
66 | _ => { | 88 | return Ok(ret); |
67 | if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) { | 89 | } |
68 | return true; | 90 | |
69 | } | 91 | // Examine the supertype and consider auto-borrowing. |
92 | match to_ty.kind(&Interner) { | ||
93 | TyKind::Raw(mt, _) => { | ||
94 | return self.coerce_ptr(from_ty, to_ty, *mt); | ||
95 | } | ||
96 | TyKind::Ref(mt, _, _) => { | ||
97 | return self.coerce_ref(from_ty, to_ty, *mt); | ||
70 | } | 98 | } |
99 | _ => {} | ||
71 | } | 100 | } |
72 | 101 | ||
73 | // Pointer weakening and function to pointer | 102 | match from_ty.kind(&Interner) { |
74 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | 103 | TyKind::FnDef(..) => { |
75 | // `*mut T` -> `*const T` | 104 | // Function items are coercible to any closure |
76 | (TyKind::Raw(_, inner), TyKind::Raw(m2 @ Mutability::Not, ..)) => { | 105 | // type; function pointers are not (that would |
77 | from_ty = TyKind::Raw(*m2, inner.clone()).intern(&Interner); | 106 | // require double indirection). |
107 | // Additionally, we permit coercion of function | ||
108 | // items to drop the unsafe qualifier. | ||
109 | self.coerce_from_fn_item(from_ty, to_ty) | ||
110 | } | ||
111 | TyKind::Function(from_fn_ptr) => { | ||
112 | // We permit coercion of fn pointers to drop the | ||
113 | // unsafe qualifier. | ||
114 | self.coerce_from_fn_pointer(from_ty.clone(), from_fn_ptr, to_ty) | ||
78 | } | 115 | } |
79 | // `&mut T` -> `&T` | 116 | TyKind::Closure(_, from_substs) => { |
80 | (TyKind::Ref(_, lt, inner), TyKind::Ref(m2 @ Mutability::Not, ..)) => { | 117 | // Non-capturing closures are coercible to |
81 | from_ty = TyKind::Ref(*m2, lt.clone(), inner.clone()).intern(&Interner); | 118 | // function pointers or unsafe function pointers. |
119 | // It cannot convert closures that require unsafe. | ||
120 | self.coerce_closure_to_fn(from_ty.clone(), from_substs, to_ty) | ||
82 | } | 121 | } |
83 | // `&T` -> `*const T` | 122 | _ => { |
84 | // `&mut T` -> `*mut T`/`*const T` | 123 | // Otherwise, just use unification rules. |
85 | (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..)) | 124 | self.unify_inner(&from_ty, to_ty) |
86 | | (TyKind::Ref(Mutability::Mut, _, substs), &TyKind::Raw(m2, ..)) => { | ||
87 | from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner); | ||
88 | } | 125 | } |
126 | } | ||
127 | } | ||
89 | 128 | ||
90 | // Illegal mutability conversion | 129 | fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult { |
91 | (TyKind::Raw(Mutability::Not, ..), TyKind::Raw(Mutability::Mut, ..)) | 130 | let (_is_ref, from_mt, from_inner) = match from_ty.kind(&Interner) { |
92 | | (TyKind::Ref(Mutability::Not, ..), TyKind::Ref(Mutability::Mut, ..)) => return false, | 131 | TyKind::Ref(mt, _, ty) => (true, mt, ty), |
132 | TyKind::Raw(mt, ty) => (false, mt, ty), | ||
133 | _ => return self.unify_inner(&from_ty, to_ty), | ||
134 | }; | ||
93 | 135 | ||
94 | // `{function_type}` -> `fn()` | 136 | coerce_mutabilities(*from_mt, to_mt)?; |
95 | (TyKind::FnDef(..), TyKind::Function { .. }) => match from_ty.callable_sig(self.db) { | 137 | |
96 | None => return false, | 138 | // Check that the types which they point at are compatible. |
97 | Some(sig) => { | 139 | let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner); |
98 | from_ty = TyBuilder::fn_ptr(sig); | 140 | // FIXME: behavior differs based on is_ref once we're computing adjustments |
99 | } | 141 | self.unify_inner(&from_raw, to_ty) |
142 | } | ||
143 | |||
144 | /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. | ||
145 | /// To match `A` with `B`, autoderef will be performed, | ||
146 | /// calling `deref`/`deref_mut` where necessary. | ||
147 | fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult { | ||
148 | let (from_mt, from_inner) = match from_ty.kind(&Interner) { | ||
149 | TyKind::Ref(mt, _, ty) => { | ||
150 | coerce_mutabilities(*mt, to_mt)?; | ||
151 | (*mt, ty.clone()) | ||
152 | } | ||
153 | _ => return self.unify_inner(&from_ty, to_ty), | ||
154 | }; | ||
155 | |||
156 | // NOTE: this code is mostly copied and adapted from rustc, and | ||
157 | // currently more complicated than necessary, carrying errors around | ||
158 | // etc.. This complication will become necessary when we actually track | ||
159 | // details of coercion errors though, so I think it's useful to leave | ||
160 | // the structure like it is. | ||
161 | |||
162 | let canonicalized = self.canonicalize(from_ty.clone()); | ||
163 | let mut autoderef = autoderef::autoderef( | ||
164 | self.db, | ||
165 | self.resolver.krate(), | ||
166 | InEnvironment { | ||
167 | goal: canonicalized.value.clone(), | ||
168 | environment: self.trait_env.env.clone(), | ||
100 | }, | 169 | }, |
170 | ); | ||
171 | let mut first_error = None; | ||
172 | let mut found = None; | ||
101 | 173 | ||
102 | (TyKind::Closure(.., substs), TyKind::Function { .. }) => { | 174 | for (autoderefs, referent_ty) in autoderef.enumerate() { |
103 | from_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone(); | 175 | if autoderefs == 0 { |
176 | // Don't let this pass, otherwise it would cause | ||
177 | // &T to autoref to &&T. | ||
178 | continue; | ||
104 | } | 179 | } |
105 | 180 | ||
106 | _ => {} | 181 | let referent_ty = canonicalized.decanonicalize_ty(referent_ty.value); |
182 | |||
183 | // At this point, we have deref'd `a` to `referent_ty`. So | ||
184 | // imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`. | ||
185 | // In the autoderef loop for `&'a mut Vec<T>`, we would get | ||
186 | // three callbacks: | ||
187 | // | ||
188 | // - `&'a mut Vec<T>` -- 0 derefs, just ignore it | ||
189 | // - `Vec<T>` -- 1 deref | ||
190 | // - `[T]` -- 2 deref | ||
191 | // | ||
192 | // At each point after the first callback, we want to | ||
193 | // check to see whether this would match out target type | ||
194 | // (`&'b mut [T]`) if we autoref'd it. We can't just | ||
195 | // compare the referent types, though, because we still | ||
196 | // have to consider the mutability. E.g., in the case | ||
197 | // we've been considering, we have an `&mut` reference, so | ||
198 | // the `T` in `[T]` needs to be unified with equality. | ||
199 | // | ||
200 | // Therefore, we construct reference types reflecting what | ||
201 | // the types will be after we do the final auto-ref and | ||
202 | // compare those. Note that this means we use the target | ||
203 | // mutability [1], since it may be that we are coercing | ||
204 | // from `&mut T` to `&U`. | ||
205 | let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc | ||
206 | let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(&Interner); | ||
207 | match self.unify_inner(&derefd_from_ty, to_ty) { | ||
208 | Ok(result) => { | ||
209 | found = Some(result); | ||
210 | break; | ||
211 | } | ||
212 | Err(err) => { | ||
213 | if first_error.is_none() { | ||
214 | first_error = Some(err); | ||
215 | } | ||
216 | } | ||
217 | } | ||
107 | } | 218 | } |
108 | 219 | ||
109 | if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { | 220 | // Extract type or return an error. We return the first error |
110 | return ret; | 221 | // we got, which should be from relating the "base" type |
222 | // (e.g., in example above, the failure from relating `Vec<T>` | ||
223 | // to the target type), since that should be the least | ||
224 | // confusing. | ||
225 | let result = match found { | ||
226 | Some(d) => d, | ||
227 | None => { | ||
228 | let err = first_error.expect("coerce_borrowed_pointer had no error"); | ||
229 | return Err(err); | ||
230 | } | ||
231 | }; | ||
232 | |||
233 | Ok(result) | ||
234 | } | ||
235 | |||
236 | /// Attempts to coerce from the type of a Rust function item into a closure | ||
237 | /// or a function pointer. | ||
238 | fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult { | ||
239 | match to_ty.kind(&Interner) { | ||
240 | TyKind::Function(b_sig) => { | ||
241 | let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig"); | ||
242 | |||
243 | // FIXME check ABI: Intrinsics are not coercible to function pointers | ||
244 | // FIXME Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396) | ||
245 | |||
246 | // FIXME rustc normalizes assoc types in the sig here, not sure if necessary | ||
247 | |||
248 | let from_sig = from_sig.to_fn_ptr(); | ||
249 | let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(&Interner); | ||
250 | let ok = self.coerce_from_safe_fn(from_fn_pointer, &from_sig, to_ty)?; | ||
251 | |||
252 | Ok(ok) | ||
253 | } | ||
254 | _ => self.unify_inner(&from_ty, to_ty), | ||
111 | } | 255 | } |
256 | } | ||
112 | 257 | ||
113 | // Auto Deref if cannot coerce | 258 | fn coerce_from_fn_pointer( |
114 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | 259 | &mut self, |
115 | // FIXME: DerefMut | 260 | from_ty: Ty, |
116 | (TyKind::Ref(.., st1), TyKind::Ref(.., st2)) => { | 261 | from_f: &FnPointer, |
117 | self.unify_autoderef_behind_ref(st1, st2) | 262 | to_ty: &Ty, |
263 | ) -> InferResult { | ||
264 | self.coerce_from_safe_fn(from_ty, from_f, to_ty) | ||
265 | } | ||
266 | |||
267 | fn coerce_from_safe_fn( | ||
268 | &mut self, | ||
269 | from_ty: Ty, | ||
270 | from_fn_ptr: &FnPointer, | ||
271 | to_ty: &Ty, | ||
272 | ) -> InferResult { | ||
273 | if let TyKind::Function(to_fn_ptr) = to_ty.kind(&Interner) { | ||
274 | if let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) = | ||
275 | (from_fn_ptr.sig.safety, to_fn_ptr.sig.safety) | ||
276 | { | ||
277 | let from_unsafe = | ||
278 | TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(&Interner); | ||
279 | return self.unify_inner(&from_unsafe, to_ty); | ||
118 | } | 280 | } |
281 | } | ||
282 | self.unify_inner(&from_ty, to_ty) | ||
283 | } | ||
119 | 284 | ||
120 | // Otherwise, normal unify | 285 | /// Attempts to coerce from the type of a non-capturing closure into a |
121 | _ => self.unify(&from_ty, to_ty), | 286 | /// function pointer. |
287 | fn coerce_closure_to_fn( | ||
288 | &mut self, | ||
289 | from_ty: Ty, | ||
290 | from_substs: &Substitution, | ||
291 | to_ty: &Ty, | ||
292 | ) -> InferResult { | ||
293 | match to_ty.kind(&Interner) { | ||
294 | TyKind::Function(fn_ty) /* if from_substs is non-capturing (FIXME) */ => { | ||
295 | // We coerce the closure, which has fn type | ||
296 | // `extern "rust-call" fn((arg0,arg1,...)) -> _` | ||
297 | // to | ||
298 | // `fn(arg0,arg1,...) -> _` | ||
299 | // or | ||
300 | // `unsafe fn(arg0,arg1,...) -> _` | ||
301 | let safety = fn_ty.sig.safety; | ||
302 | let pointer_ty = coerce_closure_fn_ty(from_substs, safety); | ||
303 | self.unify_inner(&pointer_ty, to_ty) | ||
304 | } | ||
305 | _ => self.unify_inner(&from_ty, to_ty), | ||
122 | } | 306 | } |
123 | } | 307 | } |
124 | 308 | ||
125 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` | 309 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` |
126 | /// | 310 | /// |
127 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html | 311 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html |
128 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> { | 312 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { |
313 | // These 'if' statements require some explanation. | ||
314 | // The `CoerceUnsized` trait is special - it is only | ||
315 | // possible to write `impl CoerceUnsized<B> for A` where | ||
316 | // A and B have 'matching' fields. This rules out the following | ||
317 | // two types of blanket impls: | ||
318 | // | ||
319 | // `impl<T> CoerceUnsized<T> for SomeType` | ||
320 | // `impl<T> CoerceUnsized<SomeType> for T` | ||
321 | // | ||
322 | // Both of these trigger a special `CoerceUnsized`-related error (E0376) | ||
323 | // | ||
324 | // We can take advantage of this fact to avoid performing unecessary work. | ||
325 | // If either `source` or `target` is a type variable, then any applicable impl | ||
326 | // would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`) | ||
327 | // or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for | ||
328 | // SomeType`). | ||
329 | // | ||
330 | // However, these are exactly the kinds of impls which are forbidden by | ||
331 | // the compiler! Therefore, we can be sure that coercion will always fail | ||
332 | // when either the source or target type is a type variable. This allows us | ||
333 | // to skip performing any trait selection, and immediately bail out. | ||
334 | if from_ty.is_ty_var() { | ||
335 | return Err(TypeError); | ||
336 | } | ||
337 | if to_ty.is_ty_var() { | ||
338 | return Err(TypeError); | ||
339 | } | ||
340 | |||
341 | // Handle reborrows before trying to solve `Source: CoerceUnsized<Target>`. | ||
342 | let coerce_from = match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | ||
343 | (TyKind::Ref(from_mt, _, from_inner), TyKind::Ref(to_mt, _, _)) => { | ||
344 | coerce_mutabilities(*from_mt, *to_mt)?; | ||
345 | |||
346 | let lt = static_lifetime(); | ||
347 | TyKind::Ref(*to_mt, lt, from_inner.clone()).intern(&Interner) | ||
348 | } | ||
349 | (TyKind::Ref(from_mt, _, from_inner), TyKind::Raw(to_mt, _)) => { | ||
350 | coerce_mutabilities(*from_mt, *to_mt)?; | ||
351 | |||
352 | TyKind::Raw(*to_mt, from_inner.clone()).intern(&Interner) | ||
353 | } | ||
354 | _ => from_ty.clone(), | ||
355 | }; | ||
356 | |||
129 | let krate = self.resolver.krate().unwrap(); | 357 | let krate = self.resolver.krate().unwrap(); |
130 | let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) { | 358 | let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) { |
131 | Some(LangItemTarget::TraitId(trait_)) => trait_, | 359 | Some(LangItemTarget::TraitId(trait_)) => trait_, |
132 | _ => return None, | 360 | _ => return Err(TypeError), |
133 | }; | 361 | }; |
134 | 362 | ||
135 | let trait_ref = { | 363 | let trait_ref = { |
136 | let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); | 364 | let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); |
137 | if b.remaining() != 2 { | 365 | if b.remaining() != 2 { |
138 | // The CoerceUnsized trait should have two generic params: Self and T. | 366 | // The CoerceUnsized trait should have two generic params: Self and T. |
139 | return None; | 367 | return Err(TypeError); |
140 | } | 368 | } |
141 | b.push(from_ty.clone()).push(to_ty.clone()).build() | 369 | b.push(coerce_from.clone()).push(to_ty.clone()).build() |
142 | }; | 370 | }; |
143 | 371 | ||
144 | let goal: InEnvironment<DomainGoal> = | 372 | let goal: InEnvironment<DomainGoal> = |
@@ -146,7 +374,11 @@ impl<'a> InferenceContext<'a> { | |||
146 | 374 | ||
147 | let canonicalized = self.canonicalize(goal); | 375 | let canonicalized = self.canonicalize(goal); |
148 | 376 | ||
149 | let solution = self.db.trait_solve(krate, canonicalized.value.clone())?; | 377 | // FIXME: rustc's coerce_unsized is more specialized -- it only tries to |
378 | // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the | ||
379 | // rest for later. Also, there's some logic about sized type variables. | ||
380 | // Need to find out in what cases this is necessary | ||
381 | let solution = self.db.trait_solve(krate, canonicalized.value.clone()).ok_or(TypeError)?; | ||
150 | 382 | ||
151 | match solution { | 383 | match solution { |
152 | Solution::Unique(v) => { | 384 | Solution::Unique(v) => { |
@@ -159,38 +391,39 @@ impl<'a> InferenceContext<'a> { | |||
159 | }, | 391 | }, |
160 | ); | 392 | ); |
161 | } | 393 | } |
162 | _ => return None, | 394 | _ => return Err(TypeError), |
163 | }; | 395 | }; |
164 | 396 | ||
165 | Some(true) | 397 | Ok(InferOk {}) |
166 | } | 398 | } |
399 | } | ||
167 | 400 | ||
168 | /// Unify `from_ty` to `to_ty` with optional auto Deref | 401 | fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty { |
169 | /// | 402 | let closure_sig = closure_substs.at(&Interner, 0).assert_ty_ref(&Interner).clone(); |
170 | /// Note that the parameters are already stripped the outer reference. | 403 | match closure_sig.kind(&Interner) { |
171 | fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool { | 404 | TyKind::Function(fn_ty) => TyKind::Function(FnPointer { |
172 | let canonicalized = self.canonicalize(from_ty.clone()); | 405 | num_binders: fn_ty.num_binders, |
173 | let to_ty = self.resolve_ty_shallow(&to_ty); | 406 | sig: FnSig { safety, ..fn_ty.sig }, |
174 | // FIXME: Auto DerefMut | 407 | substitution: fn_ty.substitution.clone(), |
175 | for derefed_ty in autoderef::autoderef( | 408 | }) |
176 | self.db, | 409 | .intern(&Interner), |
177 | self.resolver.krate(), | 410 | _ => TyKind::Error.intern(&Interner), |
178 | InEnvironment { | 411 | } |
179 | goal: canonicalized.value.clone(), | 412 | } |
180 | environment: self.trait_env.env.clone(), | 413 | |
181 | }, | 414 | fn safe_to_unsafe_fn_ty(fn_ty: FnPointer) -> FnPointer { |
182 | ) { | 415 | FnPointer { |
183 | let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value); | 416 | num_binders: fn_ty.num_binders, |
184 | let from_ty = self.resolve_ty_shallow(&derefed_ty); | 417 | sig: FnSig { safety: chalk_ir::Safety::Unsafe, ..fn_ty.sig }, |
185 | // Stop when constructor matches. | 418 | substitution: fn_ty.substitution, |
186 | if from_ty.equals_ctor(&to_ty) { | 419 | } |
187 | // It will not recurse to `coerce`. | 420 | } |
188 | return self.table.unify(&from_ty, &to_ty); | ||
189 | } else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) { | ||
190 | return true; | ||
191 | } | ||
192 | } | ||
193 | 421 | ||
194 | false | 422 | fn coerce_mutabilities(from: Mutability, to: Mutability) -> InferResult { |
423 | match (from, to) { | ||
424 | (Mutability::Mut, Mutability::Mut) | ||
425 | | (Mutability::Mut, Mutability::Not) | ||
426 | | (Mutability::Not, Mutability::Not) => Ok(InferOk {}), | ||
427 | (Mutability::Not, Mutability::Mut) => Err(TypeError), | ||
195 | } | 428 | } |
196 | } | 429 | } |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 9b28c76d6..3a4258e86 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -9,7 +9,7 @@ use chalk_ir::{ | |||
9 | use chalk_solve::infer::ParameterEnaVariableExt; | 9 | use chalk_solve::infer::ParameterEnaVariableExt; |
10 | use ena::unify::UnifyKey; | 10 | use ena::unify::UnifyKey; |
11 | 11 | ||
12 | use super::InferenceContext; | 12 | use super::{InferOk, InferResult, InferenceContext, TypeError}; |
13 | use crate::{ | 13 | use crate::{ |
14 | db::HirDatabase, fold_tys, static_lifetime, BoundVar, Canonical, DebruijnIndex, GenericArg, | 14 | db::HirDatabase, fold_tys, static_lifetime, BoundVar, Canonical, DebruijnIndex, GenericArg, |
15 | InferenceVar, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyKind, | 15 | InferenceVar, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyKind, |
@@ -45,7 +45,7 @@ where | |||
45 | impl<T: HasInterner<Interner = Interner>> Canonicalized<T> { | 45 | impl<T: HasInterner<Interner = Interner>> Canonicalized<T> { |
46 | pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty { | 46 | pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty { |
47 | crate::fold_free_vars(ty, |bound, _binders| { | 47 | crate::fold_free_vars(ty, |bound, _binders| { |
48 | let var = self.free_vars[bound.index]; | 48 | let var = self.free_vars[bound.index].clone(); |
49 | var.assert_ty_ref(&Interner).clone() | 49 | var.assert_ty_ref(&Interner).clone() |
50 | }) | 50 | }) |
51 | } | 51 | } |
@@ -76,7 +76,7 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> { | |||
76 | for (i, ty) in solution.value.iter(&Interner).enumerate() { | 76 | for (i, ty) in solution.value.iter(&Interner).enumerate() { |
77 | // FIXME: deal with non-type vars here -- the only problematic part is the normalization | 77 | // FIXME: deal with non-type vars here -- the only problematic part is the normalization |
78 | // and maybe we don't need that with lazy normalization? | 78 | // and maybe we don't need that with lazy normalization? |
79 | let var = self.free_vars[i]; | 79 | let var = self.free_vars[i].clone(); |
80 | // eagerly replace projections in the type; we may be getting types | 80 | // eagerly replace projections in the type; we may be getting types |
81 | // e.g. from where clauses where this hasn't happened yet | 81 | // e.g. from where clauses where this hasn't happened yet |
82 | let ty = ctx.normalize_associated_types_in( | 82 | let ty = ctx.normalize_associated_types_in( |
@@ -218,16 +218,10 @@ impl<'a> InferenceTable<'a> { | |||
218 | self.resolve_ty_as_possible_inner(&mut Vec::new(), ty) | 218 | self.resolve_ty_as_possible_inner(&mut Vec::new(), ty) |
219 | } | 219 | } |
220 | 220 | ||
221 | /// Unify two types and register new trait goals that arise from that. | ||
222 | // TODO give these two functions better names | ||
221 | pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { | 223 | pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { |
222 | let result = self.var_unification_table.relate( | 224 | let result = if let Ok(r) = self.unify_inner(ty1, ty2) { |
223 | &Interner, | ||
224 | &self.db, | ||
225 | &self.trait_env.env, | ||
226 | chalk_ir::Variance::Invariant, | ||
227 | ty1, | ||
228 | ty2, | ||
229 | ); | ||
230 | let result = if let Ok(r) = result { | ||
231 | r | 225 | r |
232 | } else { | 226 | } else { |
233 | return false; | 227 | return false; |
@@ -236,6 +230,25 @@ impl<'a> InferenceTable<'a> { | |||
236 | true | 230 | true |
237 | } | 231 | } |
238 | 232 | ||
233 | /// Unify two types and return new trait goals arising from it, so the | ||
234 | /// caller needs to deal with them. | ||
235 | pub(crate) fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty) -> InferResult { | ||
236 | match self.var_unification_table.relate( | ||
237 | &Interner, | ||
238 | &self.db, | ||
239 | &self.trait_env.env, | ||
240 | chalk_ir::Variance::Invariant, | ||
241 | ty1, | ||
242 | ty2, | ||
243 | ) { | ||
244 | Ok(result) => { | ||
245 | // TODO deal with new goals | ||
246 | Ok(InferOk {}) | ||
247 | } | ||
248 | Err(NoSolution) => Err(TypeError), | ||
249 | } | ||
250 | } | ||
251 | |||
239 | /// If `ty` is a type variable with known type, returns that type; | 252 | /// If `ty` is a type variable with known type, returns that type; |
240 | /// otherwise, return ty. | 253 | /// otherwise, return ty. |
241 | // FIXME this could probably just return Ty | 254 | // FIXME this could probably just return Ty |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 15b61bedc..179a27763 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -203,6 +203,17 @@ impl CallableSig { | |||
203 | } | 203 | } |
204 | } | 204 | } |
205 | 205 | ||
206 | pub fn to_fn_ptr(&self) -> FnPointer { | ||
207 | FnPointer { | ||
208 | num_binders: 0, | ||
209 | sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs }, | ||
210 | substitution: FnSubst(Substitution::from_iter( | ||
211 | &Interner, | ||
212 | self.params_and_return.iter().cloned(), | ||
213 | )), | ||
214 | } | ||
215 | } | ||
216 | |||
206 | pub fn params(&self) -> &[Ty] { | 217 | pub fn params(&self) -> &[Ty] { |
207 | &self.params_and_return[0..self.params_and_return.len() - 1] | 218 | &self.params_and_return[0..self.params_and_return.len() - 1] |
208 | } | 219 | } |