Repository: rust-analyzer/rust-analyzer
Pull request #524 (2019-01-13 13:48:15)

 Implement array inference
related #394

h-michael: @flodiebold Thanks. How is this?
But I also think that there may be better way of unifying.
h-michael: @flodiebold Thanks. How is this?
But I also think that there may be better way of unifying.
h-michael: @flodiebold Fixed.
Thank you for your very very detailed explanation.
I understand implementation of ra_hir crate.
h-michael: @flodiebold Thanks! Fixed it.

Patch:
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 6633388447..6e98ebc697 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -197,6 +197,9 @@ pub enum Expr {
     Tuple {
         exprs: Vec<ExprId>,
     },
+    Array {
+        exprs: Vec<ExprId>,
+    },
     Literal(Literal),
 }
 
@@ -312,7 +315,7 @@ impl Expr {
             | Expr::UnaryOp { expr, .. } => {
                 f(*expr);
             }
-            Expr::Tuple { exprs } => {
+            Expr::Tuple { exprs } | Expr::Array { exprs } => {
                 for expr in exprs {
                     f(*expr);
                 }
@@ -649,6 +652,10 @@ impl ExprCollector {
                 let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect();
                 self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr)
             }
+            ast::ExprKind::ArrayExpr(e) => {
+                let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect();
+                self.alloc_expr(Expr::Array { exprs }, syntax_ptr)
+            }
             ast::ExprKind::Literal(e) => {
                 let child = if let Some(child) = e.literal_expr() {
                     child
@@ -691,7 +698,6 @@ impl ExprCollector {
             // TODO implement HIR for these:
             ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
             ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
-            ast::ExprKind::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
             ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
         }
     }
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 85d4dc05ce..c7c0636017 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -181,11 +181,12 @@ pub enum Ty {
     /// The pointee of a string slice. Written as `str`.
     Str,
 
-    // An array with the given length. Written as `[T; n]`.
-    // Array(Ty, ty::Const),
     /// The pointee of an array slice.  Written as `[T]`.
     Slice(Arc<Ty>),
 
+    // An array with the given length. Written as `[T; n]`.
+    Array(Arc<Ty>),
+
     /// A raw pointer. Written as `*mut T` or `*const T`
     RawPtr(Arc<Ty>, Mutability),
 
@@ -276,7 +277,10 @@ impl Ty {
                 let inner_ty = Ty::from_hir(db, module, impl_block, inner);
                 Ty::RawPtr(Arc::new(inner_ty), *mutability)
             }
-            TypeRef::Array(_inner) => Ty::Unknown, // TODO
+            TypeRef::Array(inner) => {
+                let inner_ty = Ty::from_hir(db, module, impl_block, inner);
+                Ty::Array(Arc::new(inner_ty))
+            }
             TypeRef::Slice(inner) => {
                 let inner_ty = Ty::from_hir(db, module, impl_block, inner);
                 Ty::Slice(Arc::new(inner_ty))
@@ -352,7 +356,7 @@ impl Ty {
     fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
         f(self);
         match self {
-            Ty::Slice(t) => Arc::make_mut(t).walk_mut(f),
+            Ty::Slice(t) | Ty::Array(t) => Arc::make_mut(t).walk_mut(f),
             Ty::RawPtr(t, _) => Arc::make_mut(t).walk_mut(f),
             Ty::Ref(t, _) => Arc::make_mut(t).walk_mut(f),
             Ty::Tuple(ts) => {
@@ -400,7 +404,7 @@ impl fmt::Display for Ty {
             Ty::Int(t) => write!(f, "{}", t.ty_to_string()),
             Ty::Float(t) => write!(f, "{}", t.ty_to_string()),
             Ty::Str => write!(f, "str"),
-            Ty::Slice(t) => write!(f, "[{}]", t),
+            Ty::Slice(t) | Ty::Array(t) => write!(f, "[{}]", t),
             Ty::RawPtr(t, m) => write!(f, "*{}{}", m.as_keyword_for_ptr(), t),
             Ty::Ref(t, m) => write!(f, "&{}{}", m.as_keyword_for_ref(), t),
             Ty::Never => write!(f, "!"),
@@ -1102,6 +1106,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
 
                 Ty::Tuple(Arc::from(ty_vec))
             }
+            Expr::Array { exprs } => {
+                let elem_ty = match &expected.ty {
+                    Ty::Slice(inner) | Ty::Array(inner) => Ty::clone(&inner),
+                    _ => self.new_type_var(),
+                };
+
+                for expr in exprs.iter() {
+                    self.infer_expr(*expr, &Expectation::has_type(elem_ty.clone()));
+                }
+
+                Ty::Array(Arc::new(elem_ty))
+            }
             Expr::Literal(lit) => match lit {
                 Literal::Bool(..) => Ty::Bool,
                 Literal::String(..) => Ty::Ref(Arc::new(Ty::Str), Mutability::Shared),
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 3471d52264..2d9603d904 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -67,7 +67,11 @@ impl AstNode for ArrayExpr {
 }
 
 
-impl ArrayExpr {}
+impl ArrayExpr {
+    pub fn exprs(&self) -> impl Iterator<Item = &Expr> {
+        super::children(self)
+    }
+}
 
 // ArrayType
 #[derive(Debug, PartialEq, Eq, Hash)]
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index bd8c5b4110..2aaad46b1b 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -360,7 +360,9 @@ Grammar(
         "TupleExpr": (
             collections: [["exprs", "Expr"]]
         ),
-        "ArrayExpr": (),
+        "ArrayExpr": (
+            collections: [["exprs", "Expr"]]
+        ),
         "ParenExpr": (options: ["Expr"]),
         "PathExpr": (options: ["Path"]),
         "LambdaExpr": (


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 5d7bc25cc4..affd63a850 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -334,6 +334,32 @@ fn test(x: &str, y: isize) {
     );
 }
 
+#[test]
+fn infer_array() {
+    check_inference(
+        r#"
+fn test(x: &str, y: isize) {
+    let a = [x];
+    let b = [a, a];
+    let c = [b, b];
+
+    let d = [y, 1, 2, 3];
+    let d = [1, y, 2, 3];
+    let e = [y];
+    let f = [d, d];
+    let g = [e, e];
+
+    let h = [1, 2];
+    let i = ["a", "b"];
+
+    let b = [a, ["b"]];
+    let x: [u8; 0] = [];
+}
+"#,
+        "array.txt",
+    );
+}
+
 fn infer(content: &str) -> String {
     let (db, _, file_id) = MockDatabase::with_single_file(content);
     let source_file = db.source_file(file_id);
diff --git a/crates/ra_hir/src/ty/tests/data/array.txt b/crates/ra_hir/src/ty/tests/data/array.txt
new file mode 100644
index 0000000000..acdf74ba49
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/array.txt
@@ -0,0 +1,52 @@
+[9; 10) 'x': &str
+[18; 19) 'y': isize
+[28; 293) '{     ... []; }': ()
+[38; 39) 'a': [&str]
+[42; 45) '[x]': [&str]
+[43; 44) 'x': &str
+[55; 56) 'b': [[&str]]
+[59; 65) '[a, a]': [[&str]]
+[60; 61) 'a': [&str]
+[63; 64) 'a': [&str]
+[75; 76) 'c': [[[&str]]]
+[79; 85) '[b, b]': [[[&str]]]
+[80; 81) 'b': [[&str]]
+[83; 84) 'b': [[&str]]
+[96; 97) 'd': [isize]
+[100; 112) '[y, 1, 2, 3]': [isize]
+[101; 102) 'y': isize
+[104; 105) '1': isize
+[107; 108) '2': isize
+[110; 111) '3': isize
+[122; 123) 'd': [isize]
+[126; 138) '[1, y, 2, 3]': [isize]
+[127; 128) '1': isize
+[130; 131) 'y': isize
+[133; 134) '2': isize
+[136; 137) '3': isize
+[148; 149) 'e': [isize]
+[152; 155) '[y]': [isize]
+[153; 154) 'y': isize
+[165; 166) 'f': [[isize]]
+[169; 175) '[d, d]': [[isize]]
+[170; 171) 'd': [isize]
+[173; 174) 'd': [isize]
+[185; 186) 'g': [[isize]]
+[189; 195) '[e, e]': [[isize]]
+[190; 191) 'e': [isize]
+[193; 194) 'e': [isize]
+[206; 207) 'h': [i32]
+[210; 216) '[1, 2]': [i32]
+[211; 212) '1': i32
+[214; 215) '2': i32
+[226; 227) 'i': [&str]
+[230; 240) '["a", "b"]': [&str]
+[231; 234) '"a"': &str
+[236; 239) '"b"': &str
+[251; 252) 'b': [[&str]]
+[255; 265) '[a, ["b"]]': [[&str]]
+[256; 257) 'a': [&str]
+[259; 264) '["b"]': [&str]
+[260; 263) '"b"': &str
+[275; 276) 'x': [u8]
+[288; 290) '[]': [u8]