diff options
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r-- | crates/ra_hir/src/db.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir/src/function.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/mock.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/query_definitions.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 478 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/primitive.rs | 98 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 45 |
8 files changed, 643 insertions, 2 deletions
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 62cf9ab17..f0bff3c02 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -14,6 +14,7 @@ use crate::{ | |||
14 | function::FnId, | 14 | function::FnId, |
15 | module::{ModuleId, ModuleTree, ModuleSource, | 15 | module::{ModuleId, ModuleTree, ModuleSource, |
16 | nameres::{ItemMap, InputModuleItems}}, | 16 | nameres::{ItemMap, InputModuleItems}}, |
17 | ty::InferenceResult, | ||
17 | }; | 18 | }; |
18 | 19 | ||
19 | salsa::query_group! { | 20 | salsa::query_group! { |
@@ -30,6 +31,11 @@ pub trait HirDatabase: SyntaxDatabase | |||
30 | use fn query_definitions::fn_syntax; | 31 | use fn query_definitions::fn_syntax; |
31 | } | 32 | } |
32 | 33 | ||
34 | fn infer(fn_id: FnId) -> Arc<InferenceResult> { | ||
35 | type InferQuery; | ||
36 | use fn query_definitions::infer; | ||
37 | } | ||
38 | |||
33 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { | 39 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { |
34 | type SourceFileItemsQuery; | 40 | type SourceFileItemsQuery; |
35 | use fn query_definitions::file_items; | 41 | use fn query_definitions::file_items; |
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index 2925beb16..360e9e9a0 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs | |||
@@ -10,7 +10,7 @@ use ra_syntax::{ | |||
10 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, | 10 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ DefId, HirDatabase }; | 13 | use crate::{ DefId, HirDatabase, ty::InferenceResult }; |
14 | 14 | ||
15 | pub use self::scope::FnScopes; | 15 | pub use self::scope::FnScopes; |
16 | 16 | ||
@@ -35,6 +35,10 @@ impl Function { | |||
35 | let syntax = db.fn_syntax(self.fn_id); | 35 | let syntax = db.fn_syntax(self.fn_id); |
36 | FnSignatureInfo::new(syntax.borrowed()) | 36 | FnSignatureInfo::new(syntax.borrowed()) |
37 | } | 37 | } |
38 | |||
39 | pub fn infer(&self, db: &impl HirDatabase) -> Arc<InferenceResult> { | ||
40 | db.infer(self.fn_id) | ||
41 | } | ||
38 | } | 42 | } |
39 | 43 | ||
40 | #[derive(Debug, Clone)] | 44 | #[derive(Debug, Clone)] |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index f56214b47..e84f44675 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -25,6 +25,7 @@ pub mod source_binder; | |||
25 | mod krate; | 25 | mod krate; |
26 | mod module; | 26 | mod module; |
27 | mod function; | 27 | mod function; |
28 | mod ty; | ||
28 | 29 | ||
29 | use std::ops::Index; | 30 | use std::ops::Index; |
30 | 31 | ||
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 9423e6571..a9fa540d5 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -8,7 +8,7 @@ use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; | |||
8 | 8 | ||
9 | use crate::{db, DefId, DefLoc}; | 9 | use crate::{db, DefId, DefLoc}; |
10 | 10 | ||
11 | const WORKSPACE: SourceRootId = SourceRootId(0); | 11 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
12 | 12 | ||
13 | #[derive(Debug)] | 13 | #[derive(Debug)] |
14 | pub(crate) struct MockDatabase { | 14 | pub(crate) struct MockDatabase { |
@@ -182,6 +182,7 @@ salsa::database_storage! { | |||
182 | fn item_map() for db::ItemMapQuery; | 182 | fn item_map() for db::ItemMapQuery; |
183 | fn fn_syntax() for db::FnSyntaxQuery; | 183 | fn fn_syntax() for db::FnSyntaxQuery; |
184 | fn submodules() for db::SubmodulesQuery; | 184 | fn submodules() for db::SubmodulesQuery; |
185 | fn infer() for db::InferQuery; | ||
185 | } | 186 | } |
186 | } | 187 | } |
187 | } | 188 | } |
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index efaeb1525..ccbfdf028 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs | |||
@@ -19,6 +19,7 @@ use crate::{ | |||
19 | imp::Submodule, | 19 | imp::Submodule, |
20 | nameres::{InputModuleItems, ItemMap, Resolver}, | 20 | nameres::{InputModuleItems, ItemMap, Resolver}, |
21 | }, | 21 | }, |
22 | ty::{self, InferenceResult} | ||
22 | }; | 23 | }; |
23 | 24 | ||
24 | /// Resolve `FnId` to the corresponding `SyntaxNode` | 25 | /// Resolve `FnId` to the corresponding `SyntaxNode` |
@@ -35,6 +36,13 @@ pub(super) fn fn_scopes(db: &impl HirDatabase, fn_id: FnId) -> Arc<FnScopes> { | |||
35 | Arc::new(res) | 36 | Arc::new(res) |
36 | } | 37 | } |
37 | 38 | ||
39 | pub(super) fn infer(db: &impl HirDatabase, fn_id: FnId) -> Arc<InferenceResult> { | ||
40 | let syntax = db.fn_syntax(fn_id); | ||
41 | let scopes = db.fn_scopes(fn_id); | ||
42 | let res = ty::infer(db, syntax.borrowed(), scopes); | ||
43 | Arc::new(res) | ||
44 | } | ||
45 | |||
38 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { | 46 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { |
39 | let mut res = SourceFileItems::new(file_id); | 47 | let mut res = SourceFileItems::new(file_id); |
40 | let source_file = db.source_file(file_id); | 48 | let source_file = db.source_file(file_id); |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs new file mode 100644 index 000000000..36dc5d137 --- /dev/null +++ b/crates/ra_hir/src/ty.rs | |||
@@ -0,0 +1,478 @@ | |||
1 | mod primitive; | ||
2 | #[cfg(test)] | ||
3 | mod tests; | ||
4 | |||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
6 | |||
7 | use std::sync::Arc; | ||
8 | use std::collections::HashMap; | ||
9 | |||
10 | use ra_db::LocalSyntaxPtr; | ||
11 | use ra_syntax::{ | ||
12 | TextRange, TextUnit, | ||
13 | algo::visit::{visitor, Visitor}, | ||
14 | ast::{self, AstNode, DocCommentsOwner, NameOwner, LoopBodyOwner, ArgListOwner}, | ||
15 | SyntaxNodeRef | ||
16 | }; | ||
17 | |||
18 | use crate::{ | ||
19 | FnScopes, | ||
20 | db::HirDatabase, | ||
21 | arena::{Arena, Id}, | ||
22 | }; | ||
23 | |||
24 | // pub(crate) type TypeId = Id<Ty>; | ||
25 | |||
26 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
27 | pub enum Ty { | ||
28 | /// The primitive boolean type. Written as `bool`. | ||
29 | Bool, | ||
30 | |||
31 | /// The primitive character type; holds a Unicode scalar value | ||
32 | /// (a non-surrogate code point). Written as `char`. | ||
33 | Char, | ||
34 | |||
35 | /// A primitive signed integer type. For example, `i32`. | ||
36 | Int(primitive::IntTy), | ||
37 | |||
38 | /// A primitive unsigned integer type. For example, `u32`. | ||
39 | Uint(primitive::UintTy), | ||
40 | |||
41 | /// A primitive floating-point type. For example, `f64`. | ||
42 | Float(primitive::FloatTy), | ||
43 | |||
44 | /// Structures, enumerations and unions. | ||
45 | /// | ||
46 | /// Substs here, possibly against intuition, *may* contain `Param`s. | ||
47 | /// That is, even after substitution it is possible that there are type | ||
48 | /// variables. This happens when the `Adt` corresponds to an ADT | ||
49 | /// definition and not a concrete use of it. | ||
50 | // Adt(&'tcx AdtDef, &'tcx Substs<'tcx>), | ||
51 | |||
52 | // Foreign(DefId), | ||
53 | |||
54 | /// The pointee of a string slice. Written as `str`. | ||
55 | Str, | ||
56 | |||
57 | /// An array with the given length. Written as `[T; n]`. | ||
58 | // Array(Ty<'tcx>, &'tcx ty::Const<'tcx>), | ||
59 | |||
60 | /// The pointee of an array slice. Written as `[T]`. | ||
61 | Slice(TyRef), | ||
62 | |||
63 | /// A raw pointer. Written as `*mut T` or `*const T` | ||
64 | // RawPtr(TypeAndMut<'tcx>), | ||
65 | |||
66 | /// A reference; a pointer with an associated lifetime. Written as | ||
67 | /// `&'a mut T` or `&'a T`. | ||
68 | // Ref(Region<'tcx>, Ty<'tcx>, hir::Mutability), | ||
69 | |||
70 | /// The anonymous type of a function declaration/definition. Each | ||
71 | /// function has a unique type, which is output (for a function | ||
72 | /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`. | ||
73 | /// | ||
74 | /// For example the type of `bar` here: | ||
75 | /// | ||
76 | /// ```rust | ||
77 | /// fn foo() -> i32 { 1 } | ||
78 | /// let bar = foo; // bar: fn() -> i32 {foo} | ||
79 | /// ``` | ||
80 | // FnDef(DefId, &'tcx Substs<'tcx>), | ||
81 | |||
82 | /// A pointer to a function. Written as `fn() -> i32`. | ||
83 | /// | ||
84 | /// For example the type of `bar` here: | ||
85 | /// | ||
86 | /// ```rust | ||
87 | /// fn foo() -> i32 { 1 } | ||
88 | /// let bar: fn() -> i32 = foo; | ||
89 | /// ``` | ||
90 | // FnPtr(PolyFnSig<'tcx>), | ||
91 | |||
92 | /// A trait, defined with `trait`. | ||
93 | // Dynamic(Binder<&'tcx List<ExistentialPredicate<'tcx>>>, ty::Region<'tcx>), | ||
94 | |||
95 | /// The anonymous type of a closure. Used to represent the type of | ||
96 | /// `|a| a`. | ||
97 | // Closure(DefId, ClosureSubsts<'tcx>), | ||
98 | |||
99 | /// The anonymous type of a generator. Used to represent the type of | ||
100 | /// `|a| yield a`. | ||
101 | // Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability), | ||
102 | |||
103 | /// A type representin the types stored inside a generator. | ||
104 | /// This should only appear in GeneratorInteriors. | ||
105 | // GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>), | ||
106 | |||
107 | /// The never type `!` | ||
108 | Never, | ||
109 | |||
110 | /// A tuple type. For example, `(i32, bool)`. | ||
111 | Tuple(Vec<Ty>), | ||
112 | |||
113 | /// The projection of an associated type. For example, | ||
114 | /// `<T as Trait<..>>::N`. | ||
115 | // Projection(ProjectionTy<'tcx>), | ||
116 | |||
117 | /// Opaque (`impl Trait`) type found in a return type. | ||
118 | /// The `DefId` comes either from | ||
119 | /// * the `impl Trait` ast::Ty node, | ||
120 | /// * or the `existential type` declaration | ||
121 | /// The substitutions are for the generics of the function in question. | ||
122 | /// After typeck, the concrete type can be found in the `types` map. | ||
123 | // Opaque(DefId, &'tcx Substs<'tcx>), | ||
124 | |||
125 | /// A type parameter; for example, `T` in `fn f<T>(x: T) {} | ||
126 | // Param(ParamTy), | ||
127 | |||
128 | /// Bound type variable, used only when preparing a trait query. | ||
129 | // Bound(ty::DebruijnIndex, BoundTy), | ||
130 | |||
131 | /// A placeholder type - universally quantified higher-ranked type. | ||
132 | // Placeholder(ty::PlaceholderType), | ||
133 | |||
134 | /// A type variable used during type checking. | ||
135 | // Infer(InferTy), | ||
136 | |||
137 | /// A placeholder for a type which could not be computed; this is | ||
138 | /// propagated to avoid useless error messages. | ||
139 | Unknown, | ||
140 | } | ||
141 | |||
142 | type TyRef = Arc<Ty>; | ||
143 | |||
144 | impl Ty { | ||
145 | pub fn new(node: ast::TypeRef) -> Self { | ||
146 | use ra_syntax::ast::TypeRef::*; | ||
147 | match node { | ||
148 | ParenType(_inner) => Ty::Unknown, // TODO | ||
149 | TupleType(_inner) => Ty::Unknown, // TODO | ||
150 | NeverType(..) => Ty::Never, | ||
151 | PathType(_inner) => Ty::Unknown, // TODO | ||
152 | PointerType(_inner) => Ty::Unknown, // TODO | ||
153 | ArrayType(_inner) => Ty::Unknown, // TODO | ||
154 | SliceType(_inner) => Ty::Unknown, // TODO | ||
155 | ReferenceType(_inner) => Ty::Unknown, // TODO | ||
156 | PlaceholderType(_inner) => Ty::Unknown, // TODO | ||
157 | FnPointerType(_inner) => Ty::Unknown, // TODO | ||
158 | ForType(_inner) => Ty::Unknown, // TODO | ||
159 | ImplTraitType(_inner) => Ty::Unknown, // TODO | ||
160 | DynTraitType(_inner) => Ty::Unknown, // TODO | ||
161 | } | ||
162 | } | ||
163 | |||
164 | pub fn unit() -> Self { | ||
165 | Ty::Tuple(Vec::new()) | ||
166 | } | ||
167 | } | ||
168 | |||
169 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
170 | pub struct InferenceResult { | ||
171 | type_for: FxHashMap<LocalSyntaxPtr, Ty>, | ||
172 | } | ||
173 | |||
174 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
175 | pub struct InferenceContext { | ||
176 | scopes: Arc<FnScopes>, | ||
177 | // TODO unification tables... | ||
178 | type_for: FxHashMap<LocalSyntaxPtr, Ty>, | ||
179 | } | ||
180 | |||
181 | impl InferenceContext { | ||
182 | fn new(scopes: Arc<FnScopes>) -> Self { | ||
183 | InferenceContext { | ||
184 | type_for: FxHashMap::default(), | ||
185 | scopes | ||
186 | } | ||
187 | } | ||
188 | |||
189 | fn write_ty(&mut self, node: SyntaxNodeRef, ty: Ty) { | ||
190 | self.type_for.insert(LocalSyntaxPtr::new(node), ty); | ||
191 | } | ||
192 | |||
193 | fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { | ||
194 | unimplemented!() | ||
195 | } | ||
196 | |||
197 | fn infer_expr(&mut self, expr: ast::Expr) -> Ty { | ||
198 | let ty = match expr { | ||
199 | ast::Expr::IfExpr(e) => { | ||
200 | if let Some(condition) = e.condition() { | ||
201 | if let Some(e) = condition.expr() { | ||
202 | // TODO if no pat, this should be bool | ||
203 | self.infer_expr(e); | ||
204 | } | ||
205 | // TODO write type for pat | ||
206 | }; | ||
207 | let if_ty = if let Some(block) = e.then_branch() { | ||
208 | self.infer_block(block) | ||
209 | } else { | ||
210 | Ty::Unknown | ||
211 | }; | ||
212 | let else_ty = if let Some(block) = e.else_branch() { | ||
213 | self.infer_block(block) | ||
214 | } else { | ||
215 | Ty::Unknown | ||
216 | }; | ||
217 | if self.unify(&if_ty, &else_ty) { | ||
218 | // TODO actually, need to take the 'more specific' type (not unknown, never, ...) | ||
219 | if_ty | ||
220 | } else { | ||
221 | // TODO report diagnostic | ||
222 | Ty::Unknown | ||
223 | } | ||
224 | } | ||
225 | ast::Expr::BlockExpr(e) => { | ||
226 | if let Some(block) = e.block() { | ||
227 | self.infer_block(block) | ||
228 | } else { | ||
229 | Ty::Unknown | ||
230 | } | ||
231 | } | ||
232 | ast::Expr::LoopExpr(e) => { | ||
233 | if let Some(block) = e.loop_body() { | ||
234 | self.infer_block(block); | ||
235 | }; | ||
236 | // TODO never, or the type of the break param | ||
237 | Ty::Unknown | ||
238 | } | ||
239 | ast::Expr::WhileExpr(e) => { | ||
240 | if let Some(condition) = e.condition() { | ||
241 | if let Some(e) = condition.expr() { | ||
242 | // TODO if no pat, this should be bool | ||
243 | self.infer_expr(e); | ||
244 | } | ||
245 | // TODO write type for pat | ||
246 | }; | ||
247 | if let Some(block) = e.loop_body() { | ||
248 | // TODO | ||
249 | self.infer_block(block); | ||
250 | }; | ||
251 | // TODO always unit? | ||
252 | Ty::Unknown | ||
253 | } | ||
254 | ast::Expr::ForExpr(e) => { | ||
255 | if let Some(expr) = e.iterable() { | ||
256 | self.infer_expr(expr); | ||
257 | } | ||
258 | if let Some(pat) = e.pat() { | ||
259 | // TODO write type for pat | ||
260 | } | ||
261 | if let Some(block) = e.loop_body() { | ||
262 | self.infer_block(block); | ||
263 | } | ||
264 | // TODO always unit? | ||
265 | Ty::Unknown | ||
266 | } | ||
267 | ast::Expr::LambdaExpr(e) => { | ||
268 | let body_ty = if let Some(body) = e.body() { | ||
269 | self.infer_expr(body) | ||
270 | } else { | ||
271 | Ty::Unknown | ||
272 | }; | ||
273 | Ty::Unknown | ||
274 | } | ||
275 | ast::Expr::CallExpr(e) => { | ||
276 | if let Some(arg_list) = e.arg_list() { | ||
277 | for arg in arg_list.args() { | ||
278 | // TODO unify / expect argument type | ||
279 | self.infer_expr(arg); | ||
280 | } | ||
281 | } | ||
282 | Ty::Unknown | ||
283 | } | ||
284 | ast::Expr::MethodCallExpr(e) => { | ||
285 | if let Some(arg_list) = e.arg_list() { | ||
286 | for arg in arg_list.args() { | ||
287 | // TODO unify / expect argument type | ||
288 | self.infer_expr(arg); | ||
289 | } | ||
290 | } | ||
291 | Ty::Unknown | ||
292 | } | ||
293 | ast::Expr::MatchExpr(e) => { | ||
294 | let ty = if let Some(match_expr) = e.expr() { | ||
295 | self.infer_expr(match_expr) | ||
296 | } else { | ||
297 | Ty::Unknown | ||
298 | }; | ||
299 | if let Some(match_arm_list) = e.match_arm_list() { | ||
300 | for arm in match_arm_list.arms() { | ||
301 | // TODO type the bindings in pat | ||
302 | // TODO type the guard | ||
303 | let ty = if let Some(e) = arm.expr() { | ||
304 | self.infer_expr(e) | ||
305 | } else { | ||
306 | Ty::Unknown | ||
307 | }; | ||
308 | } | ||
309 | // TODO unify all the match arm types | ||
310 | Ty::Unknown | ||
311 | } else { | ||
312 | Ty::Unknown | ||
313 | } | ||
314 | } | ||
315 | ast::Expr::TupleExpr(e) => { | ||
316 | Ty::Unknown | ||
317 | } | ||
318 | ast::Expr::ArrayExpr(e) => { | ||
319 | Ty::Unknown | ||
320 | } | ||
321 | ast::Expr::PathExpr(e) => { | ||
322 | if let Some(p) = e.path() { | ||
323 | if p.qualifier().is_none() { | ||
324 | if let Some(name) = p.segment().and_then(|s| s.name_ref()) { | ||
325 | let s = self.scopes.resolve_local_name(name); | ||
326 | if let Some(scope_entry) = s { | ||
327 | if let Some(ty) = self.type_for.get(&scope_entry.ptr()) { | ||
328 | ty.clone() | ||
329 | } else { | ||
330 | // TODO introduce type variable? | ||
331 | Ty::Unknown | ||
332 | } | ||
333 | } else { | ||
334 | Ty::Unknown | ||
335 | } | ||
336 | } else { | ||
337 | Ty::Unknown | ||
338 | } | ||
339 | } else { | ||
340 | // TODO resolve path | ||
341 | Ty::Unknown | ||
342 | } | ||
343 | } else { | ||
344 | Ty::Unknown | ||
345 | } | ||
346 | } | ||
347 | ast::Expr::ContinueExpr(e) => { | ||
348 | Ty::Never | ||
349 | } | ||
350 | ast::Expr::BreakExpr(e) => { | ||
351 | Ty::Never | ||
352 | } | ||
353 | ast::Expr::ParenExpr(e) => { | ||
354 | if let Some(e) = e.expr() { | ||
355 | self.infer_expr(e) | ||
356 | } else { | ||
357 | Ty::Unknown | ||
358 | } | ||
359 | } | ||
360 | ast::Expr::Label(e) => { | ||
361 | Ty::Unknown | ||
362 | } | ||
363 | ast::Expr::ReturnExpr(e) => { | ||
364 | if let Some(e) = e.expr() { | ||
365 | // TODO unify with return type | ||
366 | self.infer_expr(e); | ||
367 | }; | ||
368 | Ty::Never | ||
369 | } | ||
370 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { | ||
371 | // Can this even occur outside of a match expression? | ||
372 | Ty::Unknown | ||
373 | } | ||
374 | ast::Expr::StructLit(e) => { | ||
375 | Ty::Unknown | ||
376 | } | ||
377 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { | ||
378 | // Can this even occur outside of a struct literal? | ||
379 | Ty::Unknown | ||
380 | } | ||
381 | ast::Expr::IndexExpr(e) => { | ||
382 | Ty::Unknown | ||
383 | } | ||
384 | ast::Expr::FieldExpr(e) => { | ||
385 | Ty::Unknown | ||
386 | } | ||
387 | ast::Expr::TryExpr(e) => { | ||
388 | let inner_ty = if let Some(e) = e.expr() { | ||
389 | self.infer_expr(e) | ||
390 | } else { | ||
391 | Ty::Unknown | ||
392 | }; | ||
393 | Ty::Unknown | ||
394 | } | ||
395 | ast::Expr::CastExpr(e) => { | ||
396 | let inner_ty = if let Some(e) = e.expr() { | ||
397 | self.infer_expr(e) | ||
398 | } else { | ||
399 | Ty::Unknown | ||
400 | }; | ||
401 | let cast_ty = e.type_ref().map(Ty::new).unwrap_or(Ty::Unknown); | ||
402 | // TODO do the coercion... | ||
403 | cast_ty | ||
404 | } | ||
405 | ast::Expr::RefExpr(e) => { | ||
406 | let inner_ty = if let Some(e) = e.expr() { | ||
407 | self.infer_expr(e) | ||
408 | } else { | ||
409 | Ty::Unknown | ||
410 | }; | ||
411 | Ty::Unknown | ||
412 | } | ||
413 | ast::Expr::PrefixExpr(e) => { | ||
414 | let inner_ty = if let Some(e) = e.expr() { | ||
415 | self.infer_expr(e) | ||
416 | } else { | ||
417 | Ty::Unknown | ||
418 | }; | ||
419 | Ty::Unknown | ||
420 | } | ||
421 | ast::Expr::RangeExpr(e) => { | ||
422 | Ty::Unknown | ||
423 | } | ||
424 | ast::Expr::BinExpr(e) => { | ||
425 | Ty::Unknown | ||
426 | } | ||
427 | ast::Expr::Literal(e) => { | ||
428 | Ty::Unknown | ||
429 | } | ||
430 | }; | ||
431 | self.write_ty(expr.syntax(), ty.clone()); | ||
432 | ty | ||
433 | } | ||
434 | |||
435 | fn infer_block(&mut self, node: ast::Block) -> Ty { | ||
436 | for stmt in node.statements() { | ||
437 | match stmt { | ||
438 | ast::Stmt::LetStmt(stmt) => { | ||
439 | if let Some(expr) = stmt.initializer() { | ||
440 | self.infer_expr(expr); | ||
441 | } | ||
442 | } | ||
443 | ast::Stmt::ExprStmt(expr_stmt) => { | ||
444 | if let Some(expr) = expr_stmt.expr() { | ||
445 | self.infer_expr(expr); | ||
446 | } | ||
447 | } | ||
448 | } | ||
449 | } | ||
450 | let ty = if let Some(expr) = node.expr() { | ||
451 | self.infer_expr(expr) | ||
452 | } else { | ||
453 | Ty::unit() | ||
454 | }; | ||
455 | self.write_ty(node.syntax(), ty.clone()); | ||
456 | ty | ||
457 | } | ||
458 | } | ||
459 | |||
460 | pub fn infer(db: &impl HirDatabase, node: ast::FnDef, scopes: Arc<FnScopes>) -> InferenceResult { | ||
461 | let mut ctx = InferenceContext::new(scopes); | ||
462 | |||
463 | for param in node.param_list().unwrap().params() { | ||
464 | let pat = param.pat().unwrap(); | ||
465 | let type_ref = param.type_ref().unwrap(); | ||
466 | let ty = Ty::new(type_ref); | ||
467 | ctx.type_for.insert(LocalSyntaxPtr::new(pat.syntax()), ty); | ||
468 | } | ||
469 | |||
470 | // TODO get Ty for node.ret_type() and pass that to infer_block as expectation | ||
471 | // (see Expectation in rustc_typeck) | ||
472 | |||
473 | ctx.infer_block(node.body().unwrap()); | ||
474 | |||
475 | // TODO 'resolve' the types: replace inference variables by their inferred results | ||
476 | |||
477 | InferenceResult { type_for: ctx.type_for } | ||
478 | } | ||
diff --git a/crates/ra_hir/src/ty/primitive.rs b/crates/ra_hir/src/ty/primitive.rs new file mode 100644 index 000000000..4a5ce5a97 --- /dev/null +++ b/crates/ra_hir/src/ty/primitive.rs | |||
@@ -0,0 +1,98 @@ | |||
1 | use std::fmt; | ||
2 | |||
3 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] | ||
4 | pub enum IntTy { | ||
5 | Isize, | ||
6 | I8, | ||
7 | I16, | ||
8 | I32, | ||
9 | I64, | ||
10 | I128, | ||
11 | } | ||
12 | |||
13 | impl fmt::Debug for IntTy { | ||
14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
15 | fmt::Display::fmt(self, f) | ||
16 | } | ||
17 | } | ||
18 | |||
19 | impl fmt::Display for IntTy { | ||
20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
21 | write!(f, "{}", self.ty_to_string()) | ||
22 | } | ||
23 | } | ||
24 | |||
25 | impl IntTy { | ||
26 | pub fn ty_to_string(&self) -> &'static str { | ||
27 | match *self { | ||
28 | IntTy::Isize => "isize", | ||
29 | IntTy::I8 => "i8", | ||
30 | IntTy::I16 => "i16", | ||
31 | IntTy::I32 => "i32", | ||
32 | IntTy::I64 => "i64", | ||
33 | IntTy::I128 => "i128", | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] | ||
39 | pub enum UintTy { | ||
40 | Usize, | ||
41 | U8, | ||
42 | U16, | ||
43 | U32, | ||
44 | U64, | ||
45 | U128, | ||
46 | } | ||
47 | |||
48 | impl UintTy { | ||
49 | pub fn ty_to_string(&self) -> &'static str { | ||
50 | match *self { | ||
51 | UintTy::Usize => "usize", | ||
52 | UintTy::U8 => "u8", | ||
53 | UintTy::U16 => "u16", | ||
54 | UintTy::U32 => "u32", | ||
55 | UintTy::U64 => "u64", | ||
56 | UintTy::U128 => "u128", | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | impl fmt::Debug for UintTy { | ||
62 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
63 | fmt::Display::fmt(self, f) | ||
64 | } | ||
65 | } | ||
66 | |||
67 | impl fmt::Display for UintTy { | ||
68 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
69 | write!(f, "{}", self.ty_to_string()) | ||
70 | } | ||
71 | } | ||
72 | |||
73 | #[derive(Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)] | ||
74 | pub enum FloatTy { | ||
75 | F32, | ||
76 | F64, | ||
77 | } | ||
78 | |||
79 | impl fmt::Debug for FloatTy { | ||
80 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
81 | fmt::Display::fmt(self, f) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | impl fmt::Display for FloatTy { | ||
86 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
87 | write!(f, "{}", self.ty_to_string()) | ||
88 | } | ||
89 | } | ||
90 | |||
91 | impl FloatTy { | ||
92 | pub fn ty_to_string(self) -> &'static str { | ||
93 | match self { | ||
94 | FloatTy::F32 => "f32", | ||
95 | FloatTy::F64 => "f64", | ||
96 | } | ||
97 | } | ||
98 | } | ||
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs new file mode 100644 index 000000000..f2466dd51 --- /dev/null +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -0,0 +1,45 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use salsa::Database; | ||
4 | use ra_db::{FilesDatabase, CrateGraph, SyntaxDatabase}; | ||
5 | use ra_syntax::{SmolStr, algo::visit::{visitor, Visitor}, ast::{self, AstNode}}; | ||
6 | use relative_path::RelativePath; | ||
7 | |||
8 | use crate::{source_binder, mock::WORKSPACE, module::ModuleSourceNode}; | ||
9 | |||
10 | use crate::{ | ||
11 | self as hir, | ||
12 | db::HirDatabase, | ||
13 | mock::MockDatabase, | ||
14 | }; | ||
15 | |||
16 | fn infer_all_fns(fixture: &str) -> () { | ||
17 | let (db, source_root) = MockDatabase::with_files(fixture); | ||
18 | for &file_id in source_root.files.values() { | ||
19 | let source_file = db.source_file(file_id); | ||
20 | for fn_def in source_file.syntax().descendants().filter_map(ast::FnDef::cast) { | ||
21 | let func = source_binder::function_from_source(&db, file_id, fn_def).unwrap().unwrap(); | ||
22 | let inference_result = func.infer(&db); | ||
23 | for (syntax_ptr, ty) in &inference_result.type_for { | ||
24 | let node = syntax_ptr.resolve(&source_file); | ||
25 | eprintln!("{} '{}': {:?}", syntax_ptr.range(), node.text(), ty); | ||
26 | } | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | |||
31 | #[test] | ||
32 | fn infer_smoke_test() { | ||
33 | let text = " | ||
34 | //- /lib.rs | ||
35 | fn foo(x: u32, y: !) -> i128 { | ||
36 | x; | ||
37 | y; | ||
38 | return 1; | ||
39 | \"hello\"; | ||
40 | 0 | ||
41 | } | ||
42 | "; | ||
43 | |||
44 | infer_all_fns(text); | ||
45 | } | ||