diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index eb5fea15324..615a1caed43 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -243,8 +243,23 @@ impl InferenceContext { self.type_for.insert(LocalSyntaxPtr::new(node), ty); } - fn unify(&mut self, _ty1: &Ty, _ty2: &Ty) -> bool { - unimplemented!() + fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> Option { + if *ty1 == Ty::Unknown { + return Some(ty2.clone()); + } + if *ty2 == Ty::Unknown { + return Some(ty1.clone()); + } + if ty1 == ty2 { + return Some(ty1.clone()); + } + // TODO implement actual unification + return None; + } + + fn unify_with_coercion(&mut self, ty1: &Ty, ty2: &Ty) -> Option { + // TODO implement coercion + self.unify(ty1, ty2) } fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Option { @@ -280,9 +295,8 @@ impl InferenceContext { } else { Ty::Unknown }; - if self.unify(&if_ty, &else_ty) { - // TODO actually, need to take the 'more specific' type (not unknown, never, ...) - if_ty + if let Some(ty) = self.unify(&if_ty, &else_ty) { + ty } else { // TODO report diagnostic Ty::Unknown @@ -455,9 +469,23 @@ impl InferenceContext { for stmt in node.statements() { match stmt { ast::Stmt::LetStmt(stmt) => { - if let Some(expr) = stmt.initializer() { - self.infer_expr(expr); - } + let decl_ty = if let Some(type_ref) = stmt.type_ref() { + Ty::new(type_ref) + } else { + Ty::Unknown + }; + let ty = if let Some(expr) = stmt.initializer() { + // TODO pass expectation + let expr_ty = self.infer_expr(expr); + self.unify_with_coercion(&expr_ty, &decl_ty) + .unwrap_or(decl_ty) + } else { + decl_ty + }; + + if let Some(pat) = stmt.pat() { + self.write_ty(pat.syntax(), ty); + }; } ast::Stmt::ExprStmt(expr_stmt) => { if let Some(expr) = expr_stmt.expr() { diff --git a/crates/ra_hir/src/ty/tests/data/0002_let.rs b/crates/ra_hir/src/ty/tests/data/0002_let.rs new file mode 100644 index 00000000000..5641da75b70 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0002_let.rs @@ -0,0 +1,5 @@ +fn test() { + let a = 1isize; + let b: usize = 1; + let c = b; +} diff --git a/crates/ra_hir/src/ty/tests/data/0002_let.txt b/crates/ra_hir/src/ty/tests/data/0002_let.txt new file mode 100644 index 00000000000..5f515ee594c --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0002_let.txt @@ -0,0 +1,7 @@ +[51; 52) '1': [unknown] +[10; 70) '{ ...= b; }': () +[24; 30) '1isize': [unknown] +[20; 21) 'a': [unknown] +[62; 63) 'c': usize +[66; 67) 'b': usize +[40; 41) 'b': usize diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 74bf4d3cc31..b15c4ef6f07 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -1561,6 +1561,10 @@ impl<'a> LetStmt<'a> { super::child_opt(self) } + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } + pub fn initializer(self) -> Option> { super::child_opt(self) } diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 29b84854a35..8dca493ee3a 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -499,6 +499,7 @@ Grammar( ), "LetStmt": ( options: [ ["pat", "Pat"], + ["type_ref", "TypeRef"], ["initializer", "Expr"], ]), "Condition": (