diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-24 14:40:11 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-24 14:40:11 +0000 |
commit | 67e768466ff2e2611eead0f30b2e9c4083c80c20 (patch) | |
tree | 8984028019837c91131fc30f60eecf8c2a457368 /crates/ra_hir/src/ty.rs | |
parent | abe09eb5edfe8f4c58baa16140acbd414635836f (diff) | |
parent | 4befde1eee5b1e2b7ddc9bf764b77f82b792c318 (diff) |
Merge #327
327: Beginnings of type inference r=flodiebold a=flodiebold
I was a bit bored, so I thought I'd try to start implementing the type system and see how far I come :wink: This is obviously still extremely WIP, only very basic stuff working, but I thought I'd post this now to get some feedback as to whether this approach makes sense at all.
There's no user-visible effect yet, but the type inference has tests similar to the ones for the parser. My next step will probably be to implement struct types, after which this could probably be used to complete fields.
I realize this may all get thrown away when/if the compiler query system gets usable, but I feel like there are lots of IDE features that could be implemented with somewhat working type inference in the meantime :smile:
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/ty.rs')
-rw-r--r-- | crates/ra_hir/src/ty.rs | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs new file mode 100644 index 000000000..c759d4c8b --- /dev/null +++ b/crates/ra_hir/src/ty.rs | |||
@@ -0,0 +1,601 @@ | |||
1 | mod primitive; | ||
2 | #[cfg(test)] | ||
3 | mod tests; | ||
4 | |||
5 | use std::sync::Arc; | ||
6 | use std::fmt; | ||
7 | |||
8 | use log; | ||
9 | use rustc_hash::{FxHashMap}; | ||
10 | |||
11 | use ra_db::{LocalSyntaxPtr, Cancelable}; | ||
12 | use ra_syntax::{ | ||
13 | SmolStr, | ||
14 | ast::{self, AstNode, LoopBodyOwner, ArgListOwner}, | ||
15 | SyntaxNodeRef | ||
16 | }; | ||
17 | |||
18 | use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase}; | ||
19 | |||
20 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
21 | pub enum Ty { | ||
22 | /// The primitive boolean type. Written as `bool`. | ||
23 | Bool, | ||
24 | |||
25 | /// The primitive character type; holds a Unicode scalar value | ||
26 | /// (a non-surrogate code point). Written as `char`. | ||
27 | Char, | ||
28 | |||
29 | /// A primitive signed integer type. For example, `i32`. | ||
30 | Int(primitive::IntTy), | ||
31 | |||
32 | /// A primitive unsigned integer type. For example, `u32`. | ||
33 | Uint(primitive::UintTy), | ||
34 | |||
35 | /// A primitive floating-point type. For example, `f64`. | ||
36 | Float(primitive::FloatTy), | ||
37 | |||
38 | // Structures, enumerations and unions. | ||
39 | // Adt(AdtDef, Substs), | ||
40 | /// The pointee of a string slice. Written as `str`. | ||
41 | Str, | ||
42 | |||
43 | // An array with the given length. Written as `[T; n]`. | ||
44 | // Array(Ty, ty::Const), | ||
45 | /// The pointee of an array slice. Written as `[T]`. | ||
46 | Slice(TyRef), | ||
47 | |||
48 | // A raw pointer. Written as `*mut T` or `*const T` | ||
49 | // RawPtr(TypeAndMut<'tcx>), | ||
50 | |||
51 | // A reference; a pointer with an associated lifetime. Written as | ||
52 | // `&'a mut T` or `&'a T`. | ||
53 | // Ref(Ty<'tcx>, hir::Mutability), | ||
54 | /// A pointer to a function. Written as `fn() -> i32`. | ||
55 | /// | ||
56 | /// For example the type of `bar` here: | ||
57 | /// | ||
58 | /// ```rust | ||
59 | /// fn foo() -> i32 { 1 } | ||
60 | /// let bar: fn() -> i32 = foo; | ||
61 | /// ``` | ||
62 | FnPtr(Arc<FnSig>), | ||
63 | |||
64 | // A trait, defined with `dyn trait`. | ||
65 | // Dynamic(), | ||
66 | /// The anonymous type of a closure. Used to represent the type of | ||
67 | /// `|a| a`. | ||
68 | // Closure(DefId, ClosureSubsts<'tcx>), | ||
69 | |||
70 | /// The anonymous type of a generator. Used to represent the type of | ||
71 | /// `|a| yield a`. | ||
72 | // Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability), | ||
73 | |||
74 | /// A type representin the types stored inside a generator. | ||
75 | /// This should only appear in GeneratorInteriors. | ||
76 | // GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>), | ||
77 | |||
78 | /// The never type `!` | ||
79 | Never, | ||
80 | |||
81 | /// A tuple type. For example, `(i32, bool)`. | ||
82 | Tuple(Vec<Ty>), | ||
83 | |||
84 | // The projection of an associated type. For example, | ||
85 | // `<T as Trait<..>>::N`. | ||
86 | // Projection(ProjectionTy), | ||
87 | |||
88 | // Opaque (`impl Trait`) type found in a return type. | ||
89 | // The `DefId` comes either from | ||
90 | // * the `impl Trait` ast::Ty node, | ||
91 | // * or the `existential type` declaration | ||
92 | // The substitutions are for the generics of the function in question. | ||
93 | // Opaque(DefId, Substs), | ||
94 | |||
95 | // A type parameter; for example, `T` in `fn f<T>(x: T) {} | ||
96 | // Param(ParamTy), | ||
97 | |||
98 | // A placeholder type - universally quantified higher-ranked type. | ||
99 | // Placeholder(ty::PlaceholderType), | ||
100 | |||
101 | // A type variable used during type checking. | ||
102 | // Infer(InferTy), | ||
103 | /// A placeholder for a type which could not be computed; this is | ||
104 | /// propagated to avoid useless error messages. | ||
105 | Unknown, | ||
106 | } | ||
107 | |||
108 | type TyRef = Arc<Ty>; | ||
109 | |||
110 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
111 | pub struct FnSig { | ||
112 | input: Vec<Ty>, | ||
113 | output: Ty, | ||
114 | } | ||
115 | |||
116 | impl Ty { | ||
117 | pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable<Self> { | ||
118 | use ra_syntax::ast::TypeRef::*; | ||
119 | Ok(match node { | ||
120 | ParenType(_inner) => Ty::Unknown, // TODO | ||
121 | TupleType(_inner) => Ty::Unknown, // TODO | ||
122 | NeverType(..) => Ty::Never, | ||
123 | PathType(inner) => { | ||
124 | let path = if let Some(p) = inner.path() { | ||
125 | p | ||
126 | } else { | ||
127 | return Ok(Ty::Unknown); | ||
128 | }; | ||
129 | if path.qualifier().is_none() { | ||
130 | let name = path | ||
131 | .segment() | ||
132 | .and_then(|s| s.name_ref()) | ||
133 | .map(|n| n.text()) | ||
134 | .unwrap_or(SmolStr::new("")); | ||
135 | if let Some(int_ty) = primitive::IntTy::from_string(&name) { | ||
136 | Ty::Int(int_ty) | ||
137 | } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { | ||
138 | Ty::Uint(uint_ty) | ||
139 | } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { | ||
140 | Ty::Float(float_ty) | ||
141 | } else { | ||
142 | // TODO | ||
143 | Ty::Unknown | ||
144 | } | ||
145 | } else { | ||
146 | // TODO | ||
147 | Ty::Unknown | ||
148 | } | ||
149 | } | ||
150 | PointerType(_inner) => Ty::Unknown, // TODO | ||
151 | ArrayType(_inner) => Ty::Unknown, // TODO | ||
152 | SliceType(_inner) => Ty::Unknown, // TODO | ||
153 | ReferenceType(_inner) => Ty::Unknown, // TODO | ||
154 | PlaceholderType(_inner) => Ty::Unknown, // TODO | ||
155 | FnPointerType(_inner) => Ty::Unknown, // TODO | ||
156 | ForType(_inner) => Ty::Unknown, // TODO | ||
157 | ImplTraitType(_inner) => Ty::Unknown, // TODO | ||
158 | DynTraitType(_inner) => Ty::Unknown, // TODO | ||
159 | }) | ||
160 | } | ||
161 | |||
162 | pub fn unit() -> Self { | ||
163 | Ty::Tuple(Vec::new()) | ||
164 | } | ||
165 | } | ||
166 | |||
167 | impl fmt::Display for Ty { | ||
168 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
169 | match self { | ||
170 | Ty::Bool => write!(f, "bool"), | ||
171 | Ty::Char => write!(f, "char"), | ||
172 | Ty::Int(t) => write!(f, "{}", t.ty_to_string()), | ||
173 | Ty::Uint(t) => write!(f, "{}", t.ty_to_string()), | ||
174 | Ty::Float(t) => write!(f, "{}", t.ty_to_string()), | ||
175 | Ty::Str => write!(f, "str"), | ||
176 | Ty::Slice(t) => write!(f, "[{}]", t), | ||
177 | Ty::Never => write!(f, "!"), | ||
178 | Ty::Tuple(ts) => { | ||
179 | write!(f, "(")?; | ||
180 | for t in ts { | ||
181 | write!(f, "{},", t)?; | ||
182 | } | ||
183 | write!(f, ")") | ||
184 | } | ||
185 | Ty::FnPtr(sig) => { | ||
186 | write!(f, "fn(")?; | ||
187 | for t in &sig.input { | ||
188 | write!(f, "{},", t)?; | ||
189 | } | ||
190 | write!(f, ") -> {}", sig.output) | ||
191 | } | ||
192 | Ty::Unknown => write!(f, "[unknown]"), | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { | ||
198 | let syntax = f.syntax(db); | ||
199 | let node = syntax.borrowed(); | ||
200 | // TODO we ignore type parameters for now | ||
201 | let input = node | ||
202 | .param_list() | ||
203 | .map(|pl| { | ||
204 | pl.params() | ||
205 | .map(|p| { | ||
206 | p.type_ref() | ||
207 | .map(|t| Ty::new(db, t)) | ||
208 | .unwrap_or(Ok(Ty::Unknown)) | ||
209 | }) | ||
210 | .collect() | ||
211 | }) | ||
212 | .unwrap_or_else(|| Ok(Vec::new()))?; | ||
213 | let output = node | ||
214 | .ret_type() | ||
215 | .and_then(|rt| rt.type_ref()) | ||
216 | .map(|t| Ty::new(db, t)) | ||
217 | .unwrap_or(Ok(Ty::Unknown))?; | ||
218 | let sig = FnSig { input, output }; | ||
219 | Ok(Ty::FnPtr(Arc::new(sig))) | ||
220 | } | ||
221 | |||
222 | // TODO this should probably be per namespace (i.e. types vs. values), since for | ||
223 | // a tuple struct `struct Foo(Bar)`, Foo has function type as a value, but | ||
224 | // defines the struct type Foo when used in the type namespace. rustc has a | ||
225 | // separate DefId for the constructor, but with the current DefId approach, that | ||
226 | // seems complicated. | ||
227 | pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | ||
228 | let def = def_id.resolve(db)?; | ||
229 | match def { | ||
230 | Def::Module(..) => { | ||
231 | log::debug!("trying to get type for module {:?}", def_id); | ||
232 | Ok(Ty::Unknown) | ||
233 | } | ||
234 | Def::Function(f) => type_for_fn(db, f), | ||
235 | Def::Item => { | ||
236 | log::debug!("trying to get type for item of unknown type {:?}", def_id); | ||
237 | Ok(Ty::Unknown) | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
243 | pub struct InferenceResult { | ||
244 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, | ||
245 | } | ||
246 | |||
247 | impl InferenceResult { | ||
248 | pub fn type_of_node(&self, node: SyntaxNodeRef) -> Option<Ty> { | ||
249 | self.type_of.get(&LocalSyntaxPtr::new(node)).cloned() | ||
250 | } | ||
251 | } | ||
252 | |||
253 | #[derive(Clone, Debug)] | ||
254 | pub struct InferenceContext<'a, D: HirDatabase> { | ||
255 | db: &'a D, | ||
256 | scopes: Arc<FnScopes>, | ||
257 | module: Module, | ||
258 | // TODO unification tables... | ||
259 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, | ||
260 | } | ||
261 | |||
262 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { | ||
263 | fn new(db: &'a D, scopes: Arc<FnScopes>, module: Module) -> Self { | ||
264 | InferenceContext { | ||
265 | type_of: FxHashMap::default(), | ||
266 | db, | ||
267 | scopes, | ||
268 | module, | ||
269 | } | ||
270 | } | ||
271 | |||
272 | fn write_ty(&mut self, node: SyntaxNodeRef, ty: Ty) { | ||
273 | self.type_of.insert(LocalSyntaxPtr::new(node), ty); | ||
274 | } | ||
275 | |||
276 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> Option<Ty> { | ||
277 | if *ty1 == Ty::Unknown { | ||
278 | return Some(ty2.clone()); | ||
279 | } | ||
280 | if *ty2 == Ty::Unknown { | ||
281 | return Some(ty1.clone()); | ||
282 | } | ||
283 | if ty1 == ty2 { | ||
284 | return Some(ty1.clone()); | ||
285 | } | ||
286 | // TODO implement actual unification | ||
287 | return None; | ||
288 | } | ||
289 | |||
290 | fn unify_with_coercion(&mut self, ty1: &Ty, ty2: &Ty) -> Option<Ty> { | ||
291 | // TODO implement coercion | ||
292 | self.unify(ty1, ty2) | ||
293 | } | ||
294 | |||
295 | fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable<Option<Ty>> { | ||
296 | let ast_path = ctry!(expr.path()); | ||
297 | let path = ctry!(Path::from_ast(ast_path)); | ||
298 | if path.is_ident() { | ||
299 | // resolve locally | ||
300 | let name = ctry!(ast_path.segment().and_then(|s| s.name_ref())); | ||
301 | if let Some(scope_entry) = self.scopes.resolve_local_name(name) { | ||
302 | let ty = ctry!(self.type_of.get(&scope_entry.ptr())); | ||
303 | return Ok(Some(ty.clone())); | ||
304 | }; | ||
305 | }; | ||
306 | |||
307 | // resolve in module | ||
308 | let resolved = ctry!(self.module.resolve_path(self.db, path)?); | ||
309 | let ty = self.db.type_for_def(resolved)?; | ||
310 | // TODO we will need to add type variables for type parameters etc. here | ||
311 | Ok(Some(ty)) | ||
312 | } | ||
313 | |||
314 | fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> { | ||
315 | let ty = match expr { | ||
316 | ast::Expr::IfExpr(e) => { | ||
317 | if let Some(condition) = e.condition() { | ||
318 | if let Some(e) = condition.expr() { | ||
319 | // TODO if no pat, this should be bool | ||
320 | self.infer_expr(e)?; | ||
321 | } | ||
322 | // TODO write type for pat | ||
323 | }; | ||
324 | let if_ty = if let Some(block) = e.then_branch() { | ||
325 | self.infer_block(block)? | ||
326 | } else { | ||
327 | Ty::Unknown | ||
328 | }; | ||
329 | let else_ty = if let Some(block) = e.else_branch() { | ||
330 | self.infer_block(block)? | ||
331 | } else { | ||
332 | Ty::Unknown | ||
333 | }; | ||
334 | if let Some(ty) = self.unify(&if_ty, &else_ty) { | ||
335 | ty | ||
336 | } else { | ||
337 | // TODO report diagnostic | ||
338 | Ty::Unknown | ||
339 | } | ||
340 | } | ||
341 | ast::Expr::BlockExpr(e) => { | ||
342 | if let Some(block) = e.block() { | ||
343 | self.infer_block(block)? | ||
344 | } else { | ||
345 | Ty::Unknown | ||
346 | } | ||
347 | } | ||
348 | ast::Expr::LoopExpr(e) => { | ||
349 | if let Some(block) = e.loop_body() { | ||
350 | self.infer_block(block)?; | ||
351 | }; | ||
352 | // TODO never, or the type of the break param | ||
353 | Ty::Unknown | ||
354 | } | ||
355 | ast::Expr::WhileExpr(e) => { | ||
356 | if let Some(condition) = e.condition() { | ||
357 | if let Some(e) = condition.expr() { | ||
358 | // TODO if no pat, this should be bool | ||
359 | self.infer_expr(e)?; | ||
360 | } | ||
361 | // TODO write type for pat | ||
362 | }; | ||
363 | if let Some(block) = e.loop_body() { | ||
364 | // TODO | ||
365 | self.infer_block(block)?; | ||
366 | }; | ||
367 | // TODO always unit? | ||
368 | Ty::Unknown | ||
369 | } | ||
370 | ast::Expr::ForExpr(e) => { | ||
371 | if let Some(expr) = e.iterable() { | ||
372 | self.infer_expr(expr)?; | ||
373 | } | ||
374 | if let Some(_pat) = e.pat() { | ||
375 | // TODO write type for pat | ||
376 | } | ||
377 | if let Some(block) = e.loop_body() { | ||
378 | self.infer_block(block)?; | ||
379 | } | ||
380 | // TODO always unit? | ||
381 | Ty::Unknown | ||
382 | } | ||
383 | ast::Expr::LambdaExpr(e) => { | ||
384 | let _body_ty = if let Some(body) = e.body() { | ||
385 | self.infer_expr(body)? | ||
386 | } else { | ||
387 | Ty::Unknown | ||
388 | }; | ||
389 | Ty::Unknown | ||
390 | } | ||
391 | ast::Expr::CallExpr(e) => { | ||
392 | let callee_ty = if let Some(e) = e.expr() { | ||
393 | self.infer_expr(e)? | ||
394 | } else { | ||
395 | Ty::Unknown | ||
396 | }; | ||
397 | if let Some(arg_list) = e.arg_list() { | ||
398 | for arg in arg_list.args() { | ||
399 | // TODO unify / expect argument type | ||
400 | self.infer_expr(arg)?; | ||
401 | } | ||
402 | } | ||
403 | match callee_ty { | ||
404 | Ty::FnPtr(sig) => sig.output.clone(), | ||
405 | _ => { | ||
406 | // not callable | ||
407 | // TODO report an error? | ||
408 | Ty::Unknown | ||
409 | } | ||
410 | } | ||
411 | } | ||
412 | ast::Expr::MethodCallExpr(e) => { | ||
413 | let _receiver_ty = if let Some(e) = e.expr() { | ||
414 | self.infer_expr(e)? | ||
415 | } else { | ||
416 | Ty::Unknown | ||
417 | }; | ||
418 | if let Some(arg_list) = e.arg_list() { | ||
419 | for arg in arg_list.args() { | ||
420 | // TODO unify / expect argument type | ||
421 | self.infer_expr(arg)?; | ||
422 | } | ||
423 | } | ||
424 | Ty::Unknown | ||
425 | } | ||
426 | ast::Expr::MatchExpr(e) => { | ||
427 | let _ty = if let Some(match_expr) = e.expr() { | ||
428 | self.infer_expr(match_expr)? | ||
429 | } else { | ||
430 | Ty::Unknown | ||
431 | }; | ||
432 | if let Some(match_arm_list) = e.match_arm_list() { | ||
433 | for arm in match_arm_list.arms() { | ||
434 | // TODO type the bindings in pat | ||
435 | // TODO type the guard | ||
436 | let _ty = if let Some(e) = arm.expr() { | ||
437 | self.infer_expr(e)? | ||
438 | } else { | ||
439 | Ty::Unknown | ||
440 | }; | ||
441 | } | ||
442 | // TODO unify all the match arm types | ||
443 | Ty::Unknown | ||
444 | } else { | ||
445 | Ty::Unknown | ||
446 | } | ||
447 | } | ||
448 | ast::Expr::TupleExpr(_e) => Ty::Unknown, | ||
449 | ast::Expr::ArrayExpr(_e) => Ty::Unknown, | ||
450 | ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown), | ||
451 | ast::Expr::ContinueExpr(_e) => Ty::Never, | ||
452 | ast::Expr::BreakExpr(_e) => Ty::Never, | ||
453 | ast::Expr::ParenExpr(e) => { | ||
454 | if let Some(e) = e.expr() { | ||
455 | self.infer_expr(e)? | ||
456 | } else { | ||
457 | Ty::Unknown | ||
458 | } | ||
459 | } | ||
460 | ast::Expr::Label(_e) => Ty::Unknown, | ||
461 | ast::Expr::ReturnExpr(e) => { | ||
462 | if let Some(e) = e.expr() { | ||
463 | // TODO unify with return type | ||
464 | self.infer_expr(e)?; | ||
465 | }; | ||
466 | Ty::Never | ||
467 | } | ||
468 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { | ||
469 | // Can this even occur outside of a match expression? | ||
470 | Ty::Unknown | ||
471 | } | ||
472 | ast::Expr::StructLit(_e) => Ty::Unknown, | ||
473 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { | ||
474 | // Can this even occur outside of a struct literal? | ||
475 | Ty::Unknown | ||
476 | } | ||
477 | ast::Expr::IndexExpr(_e) => Ty::Unknown, | ||
478 | ast::Expr::FieldExpr(_e) => Ty::Unknown, | ||
479 | ast::Expr::TryExpr(e) => { | ||
480 | let _inner_ty = if let Some(e) = e.expr() { | ||
481 | self.infer_expr(e)? | ||
482 | } else { | ||
483 | Ty::Unknown | ||
484 | }; | ||
485 | Ty::Unknown | ||
486 | } | ||
487 | ast::Expr::CastExpr(e) => { | ||
488 | let _inner_ty = if let Some(e) = e.expr() { | ||
489 | self.infer_expr(e)? | ||
490 | } else { | ||
491 | Ty::Unknown | ||
492 | }; | ||
493 | let cast_ty = e | ||
494 | .type_ref() | ||
495 | .map(|t| Ty::new(self.db, t)) | ||
496 | .unwrap_or(Ok(Ty::Unknown))?; | ||
497 | // TODO do the coercion... | ||
498 | cast_ty | ||
499 | } | ||
500 | ast::Expr::RefExpr(e) => { | ||
501 | let _inner_ty = if let Some(e) = e.expr() { | ||
502 | self.infer_expr(e)? | ||
503 | } else { | ||
504 | Ty::Unknown | ||
505 | }; | ||
506 | Ty::Unknown | ||
507 | } | ||
508 | ast::Expr::PrefixExpr(e) => { | ||
509 | let _inner_ty = if let Some(e) = e.expr() { | ||
510 | self.infer_expr(e)? | ||
511 | } else { | ||
512 | Ty::Unknown | ||
513 | }; | ||
514 | Ty::Unknown | ||
515 | } | ||
516 | ast::Expr::RangeExpr(_e) => Ty::Unknown, | ||
517 | ast::Expr::BinExpr(_e) => Ty::Unknown, | ||
518 | ast::Expr::Literal(_e) => Ty::Unknown, | ||
519 | }; | ||
520 | self.write_ty(expr.syntax(), ty.clone()); | ||
521 | Ok(ty) | ||
522 | } | ||
523 | |||
524 | fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> { | ||
525 | for stmt in node.statements() { | ||
526 | match stmt { | ||
527 | ast::Stmt::LetStmt(stmt) => { | ||
528 | let decl_ty = if let Some(type_ref) = stmt.type_ref() { | ||
529 | Ty::new(self.db, type_ref)? | ||
530 | } else { | ||
531 | Ty::Unknown | ||
532 | }; | ||
533 | let ty = if let Some(expr) = stmt.initializer() { | ||
534 | // TODO pass expectation | ||
535 | let expr_ty = self.infer_expr(expr)?; | ||
536 | self.unify_with_coercion(&expr_ty, &decl_ty) | ||
537 | .unwrap_or(decl_ty) | ||
538 | } else { | ||
539 | decl_ty | ||
540 | }; | ||
541 | |||
542 | if let Some(pat) = stmt.pat() { | ||
543 | self.write_ty(pat.syntax(), ty); | ||
544 | }; | ||
545 | } | ||
546 | ast::Stmt::ExprStmt(expr_stmt) => { | ||
547 | if let Some(expr) = expr_stmt.expr() { | ||
548 | self.infer_expr(expr)?; | ||
549 | } | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | let ty = if let Some(expr) = node.expr() { | ||
554 | self.infer_expr(expr)? | ||
555 | } else { | ||
556 | Ty::unit() | ||
557 | }; | ||
558 | self.write_ty(node.syntax(), ty.clone()); | ||
559 | Ok(ty) | ||
560 | } | ||
561 | } | ||
562 | |||
563 | pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceResult> { | ||
564 | let scopes = function.scopes(db); | ||
565 | let module = function.module(db)?; | ||
566 | let mut ctx = InferenceContext::new(db, scopes, module); | ||
567 | |||
568 | let syntax = function.syntax(db); | ||
569 | let node = syntax.borrowed(); | ||
570 | |||
571 | if let Some(param_list) = node.param_list() { | ||
572 | for param in param_list.params() { | ||
573 | let pat = if let Some(pat) = param.pat() { | ||
574 | pat | ||
575 | } else { | ||
576 | continue; | ||
577 | }; | ||
578 | if let Some(type_ref) = param.type_ref() { | ||
579 | let ty = Ty::new(db, type_ref)?; | ||
580 | ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); | ||
581 | } else { | ||
582 | // TODO self param | ||
583 | ctx.type_of | ||
584 | .insert(LocalSyntaxPtr::new(pat.syntax()), Ty::Unknown); | ||
585 | }; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | // TODO get Ty for node.ret_type() and pass that to infer_block as expectation | ||
590 | // (see Expectation in rustc_typeck) | ||
591 | |||
592 | if let Some(block) = node.body() { | ||
593 | ctx.infer_block(block)?; | ||
594 | } | ||
595 | |||
596 | // TODO 'resolve' the types: replace inference variables by their inferred results | ||
597 | |||
598 | Ok(InferenceResult { | ||
599 | type_of: ctx.type_of, | ||
600 | }) | ||
601 | } | ||