]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir_def/src/type_ref.rs
Add lowering of array lengths in types
[rust.git] / crates / hir_def / src / type_ref.rs
index 4c24aae94f3828ceeda11c26761916e66489abca..00c09a23dfcdca9efde0b72d5a6f741135164c27 100644 (file)
@@ -1,6 +1,8 @@
 //! HIR for references to types. Paths in these are not yet resolved. They can
 //! be directly created from an ast::TypeRef, without further queries.
-use hir_expand::name::Name;
+
+use hir_expand::{name::Name, AstId, InFile};
+use std::convert::TryInto;
 use syntax::ast;
 
 use crate::{body::LowerCtx, path::Path};
@@ -68,6 +70,7 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
         }
     }
 }
+
 /// Compare ty::Ty
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum TypeRef {
@@ -77,13 +80,14 @@ pub enum TypeRef {
     Path(Path),
     RawPtr(Box<TypeRef>, Mutability),
     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
-    Array(Box<TypeRef> /*, Expr*/),
+    Array(Box<TypeRef>, ConstScalar),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
     Fn(Vec<TypeRef>, bool /*varargs*/),
     // For
     ImplTrait(Vec<TypeBound>),
     DynTrait(Vec<TypeBound>),
+    Macro(AstId<ast::MacroCall>),
     Error,
 }
 
@@ -116,7 +120,7 @@ pub enum TypeBound {
 
 impl TypeRef {
     /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
-    pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
+    pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
         match node {
             ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
             ast::Type::TupleType(inner) => {
@@ -137,7 +141,12 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
                 TypeRef::RawPtr(Box::new(inner_ty), mutability)
             }
             ast::Type::ArrayType(inner) => {
-                TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
+                let len = inner
+                    .expr()
+                    .map(ConstScalar::usize_from_literal_expr)
+                    .unwrap_or(ConstScalar::Unknown);
+
+                TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len)
             }
             ast::Type::SliceType(inner) => {
                 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
@@ -176,8 +185,13 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
             ast::Type::DynTraitType(inner) => {
                 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
             }
-            // FIXME: Macros in type position are not yet supported.
-            ast::Type::MacroType(_) => TypeRef::Error,
+            ast::Type::MacroType(mt) => match mt.macro_call() {
+                Some(mc) => ctx
+                    .ast_id(&mc)
+                    .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
+                    .unwrap_or(TypeRef::Error),
+                None => TypeRef::Error,
+            },
         }
     }
 
@@ -204,7 +218,7 @@ fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
                 }
                 TypeRef::RawPtr(type_ref, _)
                 | TypeRef::Reference(type_ref, ..)
-                | TypeRef::Array(type_ref)
+                | TypeRef::Array(type_ref, _)
                 | TypeRef::Slice(type_ref) => go(&type_ref, f),
                 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
                     for bound in bounds {
@@ -215,7 +229,7 @@ fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
                     }
                 }
                 TypeRef::Path(path) => go_path(path, f),
-                TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {}
+                TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
             };
         }
 
@@ -290,3 +304,44 @@ pub fn as_path(&self) -> Option<&Path> {
         }
     }
 }
+
+/// A concrete constant value
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum ConstScalar {
+    // for now, we only support the trivial case of constant evaluating the length of an array
+    // Note that this is u64 because the target usize may be bigger than our usize
+    Usize(u64),
+
+    /// Case of an unknown value that rustc might know but we don't
+    Unknown,
+}
+
+impl std::fmt::Display for ConstScalar {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        match self {
+            ConstScalar::Usize(us) => write!(fmt, "{}", us),
+            ConstScalar::Unknown => write!(fmt, "_"),
+        }
+    }
+}
+
+impl ConstScalar {
+    fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
+        match expr {
+            ast::Expr::Literal(lit) => {
+                let lkind = lit.kind();
+                match lkind {
+                    ast::LiteralKind::IntNumber(num)
+                        if num.suffix() == None || num.suffix() == Some("usize") =>
+                    {
+                        num.value().and_then(|v| v.try_into().ok())
+                    }
+                    _ => None,
+                }
+            }
+            _ => None,
+        }
+        .map(ConstScalar::Usize)
+        .unwrap_or(ConstScalar::Unknown)
+    }
+}