aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty.rs
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-12-24 14:40:11 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-12-24 14:40:11 +0000
commit67e768466ff2e2611eead0f30b2e9c4083c80c20 (patch)
tree8984028019837c91131fc30f60eecf8c2a457368 /crates/ra_hir/src/ty.rs
parentabe09eb5edfe8f4c58baa16140acbd414635836f (diff)
parent4befde1eee5b1e2b7ddc9bf764b77f82b792c318 (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.rs601
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 @@
1mod primitive;
2#[cfg(test)]
3mod tests;
4
5use std::sync::Arc;
6use std::fmt;
7
8use log;
9use rustc_hash::{FxHashMap};
10
11use ra_db::{LocalSyntaxPtr, Cancelable};
12use ra_syntax::{
13 SmolStr,
14 ast::{self, AstNode, LoopBodyOwner, ArgListOwner},
15 SyntaxNodeRef
16};
17
18use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase};
19
20#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
21pub 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
108type TyRef = Arc<Ty>;
109
110#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
111pub struct FnSig {
112 input: Vec<Ty>,
113 output: Ty,
114}
115
116impl 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
167impl 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
197pub 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.
227pub 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)]
243pub struct InferenceResult {
244 type_of: FxHashMap<LocalSyntaxPtr, Ty>,
245}
246
247impl 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)]
254pub 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
262impl<'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
563pub 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}