diff options
author | Florian Diebold <[email protected]> | 2019-01-06 18:51:42 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-01-07 13:54:23 +0000 |
commit | 7bb279b365e54ee0051e09ead5aa157ff6be917b (patch) | |
tree | 7a495d68453ea1e3c7a726e97bbbe19f9bc90532 /crates/ra_hir/src/ty.rs | |
parent | a6071c9f4c8441b4b8f2e970bc055d66cc9be5f0 (diff) |
Implement autoderef for field accesses
Diffstat (limited to 'crates/ra_hir/src/ty.rs')
-rw-r--r-- | crates/ra_hir/src/ty.rs | 72 |
1 files changed, 50 insertions, 22 deletions
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d57990cd2..bba8527b7 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -13,6 +13,7 @@ | |||
13 | //! the union-find implementation from the `ena` crate, which is extracted from | 13 | //! the union-find implementation from the `ena` crate, which is extracted from |
14 | //! rustc. | 14 | //! rustc. |
15 | 15 | ||
16 | mod autoderef; | ||
16 | mod primitive; | 17 | mod primitive; |
17 | #[cfg(test)] | 18 | #[cfg(test)] |
18 | mod tests; | 19 | mod tests; |
@@ -36,6 +37,14 @@ use crate::{ | |||
36 | expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement}, | 37 | expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement}, |
37 | }; | 38 | }; |
38 | 39 | ||
40 | fn transpose<T>(x: Cancelable<Option<T>>) -> Option<Cancelable<T>> { | ||
41 | match x { | ||
42 | Ok(Some(t)) => Some(Ok(t)), | ||
43 | Ok(None) => None, | ||
44 | Err(e) => Some(Err(e)), | ||
45 | } | ||
46 | } | ||
47 | |||
39 | /// The ID of a type variable. | 48 | /// The ID of a type variable. |
40 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | 49 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
41 | pub struct TypeVarId(u32); | 50 | pub struct TypeVarId(u32); |
@@ -357,6 +366,14 @@ impl Ty { | |||
357 | }); | 366 | }); |
358 | self | 367 | self |
359 | } | 368 | } |
369 | |||
370 | fn builtin_deref(&self) -> Option<Ty> { | ||
371 | match self { | ||
372 | Ty::Ref(t, _) => Some(Ty::clone(t)), | ||
373 | Ty::RawPtr(t, _) => Some(Ty::clone(t)), | ||
374 | _ => None, | ||
375 | } | ||
376 | } | ||
360 | } | 377 | } |
361 | 378 | ||
362 | impl fmt::Display for Ty { | 379 | impl fmt::Display for Ty { |
@@ -443,7 +460,11 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T | |||
443 | } | 460 | } |
444 | } | 461 | } |
445 | 462 | ||
446 | pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Cancelable<Ty> { | 463 | pub(super) fn type_for_field( |
464 | db: &impl HirDatabase, | ||
465 | def_id: DefId, | ||
466 | field: Name, | ||
467 | ) -> Cancelable<Option<Ty>> { | ||
447 | let def = def_id.resolve(db)?; | 468 | let def = def_id.resolve(db)?; |
448 | let variant_data = match def { | 469 | let variant_data = match def { |
449 | Def::Struct(s) => { | 470 | Def::Struct(s) => { |
@@ -459,12 +480,13 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) | |||
459 | }; | 480 | }; |
460 | let module = def_id.module(db)?; | 481 | let module = def_id.module(db)?; |
461 | let impl_block = def_id.impl_block(db)?; | 482 | let impl_block = def_id.impl_block(db)?; |
462 | let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) { | 483 | let type_ref = ctry!(variant_data.get_field_type_ref(&field)); |
463 | tr | 484 | Ok(Some(Ty::from_hir( |
464 | } else { | 485 | db, |
465 | return Ok(Ty::Unknown); | 486 | &module, |
466 | }; | 487 | impl_block.as_ref(), |
467 | Ty::from_hir(db, &module, impl_block.as_ref(), &type_ref) | 488 | &type_ref, |
489 | )?)) | ||
468 | } | 490 | } |
469 | 491 | ||
470 | /// The result of type inference: A mapping from expressions and patterns to types. | 492 | /// The result of type inference: A mapping from expressions and patterns to types. |
@@ -802,7 +824,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
802 | let (ty, def_id) = self.resolve_variant(path.as_ref())?; | 824 | let (ty, def_id) = self.resolve_variant(path.as_ref())?; |
803 | for field in fields { | 825 | for field in fields { |
804 | let field_ty = if let Some(def_id) = def_id { | 826 | let field_ty = if let Some(def_id) = def_id { |
805 | self.db.type_for_field(def_id, field.name.clone())? | 827 | self.db |
828 | .type_for_field(def_id, field.name.clone())? | ||
829 | .unwrap_or(Ty::Unknown) | ||
806 | } else { | 830 | } else { |
807 | Ty::Unknown | 831 | Ty::Unknown |
808 | }; | 832 | }; |
@@ -815,15 +839,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
815 | } | 839 | } |
816 | Expr::Field { expr, name } => { | 840 | Expr::Field { expr, name } => { |
817 | let receiver_ty = self.infer_expr(*expr, &Expectation::none())?; | 841 | let receiver_ty = self.infer_expr(*expr, &Expectation::none())?; |
818 | let ty = match receiver_ty { | 842 | let ty = receiver_ty |
819 | Ty::Tuple(fields) => { | 843 | .autoderef(self.db) |
820 | let i = name.to_string().parse::<usize>().ok(); | 844 | .find_map(|derefed_ty| match derefed_ty { |
821 | i.and_then(|i| fields.get(i).cloned()) | 845 | // this is more complicated than necessary because type_for_field is cancelable |
822 | .unwrap_or(Ty::Unknown) | 846 | Ty::Tuple(fields) => { |
823 | } | 847 | let i = name.to_string().parse::<usize>().ok(); |
824 | Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, name.clone())?, | 848 | i.and_then(|i| fields.get(i).cloned()).map(Ok) |
825 | _ => Ty::Unknown, | 849 | } |
826 | }; | 850 | Ty::Adt { def_id, .. } => { |
851 | transpose(self.db.type_for_field(def_id, name.clone())) | ||
852 | } | ||
853 | _ => None, | ||
854 | }) | ||
855 | .unwrap_or(Ok(Ty::Unknown))?; | ||
827 | self.insert_type_vars(ty) | 856 | self.insert_type_vars(ty) |
828 | } | 857 | } |
829 | Expr::Try { expr } => { | 858 | Expr::Try { expr } => { |
@@ -848,12 +877,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
848 | let inner_ty = self.infer_expr(*expr, &Expectation::none())?; | 877 | let inner_ty = self.infer_expr(*expr, &Expectation::none())?; |
849 | match op { | 878 | match op { |
850 | Some(UnaryOp::Deref) => { | 879 | Some(UnaryOp::Deref) => { |
851 | match inner_ty { | 880 | if let Some(derefed_ty) = inner_ty.builtin_deref() { |
852 | // builtin deref: | 881 | derefed_ty |
853 | Ty::Ref(ref_inner, _) => (*ref_inner).clone(), | 882 | } else { |
854 | Ty::RawPtr(ptr_inner, _) => (*ptr_inner).clone(), | ||
855 | // TODO Deref::deref | 883 | // TODO Deref::deref |
856 | _ => Ty::Unknown, | 884 | Ty::Unknown |
857 | } | 885 | } |
858 | } | 886 | } |
859 | _ => Ty::Unknown, | 887 | _ => Ty::Unknown, |