]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir_def/src/type_ref.rs
parameters.split_last()
[rust.git] / crates / hir_def / src / type_ref.rs
index 00c09a23dfcdca9efde0b72d5a6f741135164c27..ee8ef6caa306a1dc72e8db72441741b94a09aa01 100644 (file)
@@ -1,11 +1,14 @@
 //! 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, AstId, InFile};
+use hir_expand::{
+    name::{AsName, Name},
+    AstId, InFile,
+};
 use std::convert::TryInto;
-use syntax::ast;
+use syntax::ast::{self, HasName};
 
-use crate::{body::LowerCtx, path::Path};
+use crate::{body::LowerCtx, intern::Interned, path::Path};
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub enum Mutability {
@@ -72,6 +75,10 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
 }
 
 /// Compare ty::Ty
+///
+/// Note: Most users of `TypeRef` that end up in the salsa database intern it using
+/// `Interned<TypeRef>` to save space. But notably, nested `TypeRef`s are not interned, since that
+/// does not seem to save any noticeable amount of memory.
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum TypeRef {
     Never,
@@ -80,13 +87,15 @@ pub enum TypeRef {
     Path(Path),
     RawPtr(Box<TypeRef>, Mutability),
     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
+    // FIXME: for full const generics, the latter element (length) here is going to have to be an
+    // expression that is further lowered later in hir_ty.
     Array(Box<TypeRef>, ConstScalar),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
-    Fn(Vec<TypeRef>, bool /*varargs*/),
+    Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/),
     // For
-    ImplTrait(Vec<TypeBound>),
-    DynTrait(Vec<TypeBound>),
+    ImplTrait(Vec<Interned<TypeBound>>),
+    DynTrait(Vec<Interned<TypeBound>>),
     Macro(AstId<ast::MacroCall>),
     Error,
 }
@@ -112,17 +121,25 @@ pub fn missing() -> LifetimeRef {
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum TypeBound {
-    Path(Path),
-    // ForLifetime(Vec<LifetimeRef>, Path), FIXME ForLifetime
+    Path(Path, TraitBoundModifier),
+    ForLifetime(Box<[Name]>, Path),
     Lifetime(LifetimeRef),
     Error,
 }
 
+/// A modifier on a bound, currently this is only used for `?Sized`, where the
+/// modifier is `Maybe`.
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+pub enum TraitBoundModifier {
+    None,
+    Maybe,
+}
+
 impl TypeRef {
     /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
     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::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
             ast::Type::TupleType(inner) => {
                 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
             }
@@ -136,23 +153,27 @@ pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
                     .unwrap_or(TypeRef::Error)
             }
             ast::Type::PtrType(inner) => {
-                let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
+                let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
                 TypeRef::RawPtr(Box::new(inner_ty), mutability)
             }
             ast::Type::ArrayType(inner) => {
+                // FIXME: This is a hack. We should probably reuse the machinery of
+                // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
+                // `hir_ty` level, which would allow knowing the type of:
+                // let v: [u8; 2 + 2] = [0u8; 4];
                 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)
+                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())))
+                TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
             }
             ast::Type::RefType(inner) => {
-                let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
+                let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
                 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
                 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
@@ -170,15 +191,26 @@ pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
                         is_varargs = param.dotdotdot_token().is_some();
                     }
 
-                    pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect()
+                    pl.params()
+                        .map(|it| {
+                            let type_ref = TypeRef::from_ast_opt(ctx, it.ty());
+                            let name = match it.pat() {
+                                Some(ast::Pat::IdentPat(it)) => Some(
+                                    it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing),
+                                ),
+                                _ => None,
+                            };
+                            (name, type_ref)
+                        })
+                        .collect()
                 } else {
                     Vec::new()
                 };
-                params.push(ret_ty);
+                params.push((None, ret_ty));
                 TypeRef::Fn(params, is_varargs)
             }
             // for types are close enough for our purposes to the inner type for now...
-            ast::Type::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
+            ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
             ast::Type::ImplTraitType(inner) => {
                 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
             }
@@ -196,10 +228,9 @@ pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
     }
 
     pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
-        if let Some(node) = node {
-            TypeRef::from_ast(ctx, node)
-        } else {
-            TypeRef::Error
+        match node {
+            Some(node) => TypeRef::from_ast(ctx, node),
+            None => TypeRef::Error,
         }
     }
 
@@ -213,17 +244,20 @@ pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
         fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
             f(type_ref);
             match type_ref {
-                TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
-                    types.iter().for_each(|t| go(t, f))
+                TypeRef::Fn(params, _) => {
+                    params.iter().for_each(|(_, param_type)| go(&param_type, f))
                 }
+                TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
                 TypeRef::RawPtr(type_ref, _)
                 | TypeRef::Reference(type_ref, ..)
                 | TypeRef::Array(type_ref, _)
-                | TypeRef::Slice(type_ref) => go(&type_ref, f),
+                | TypeRef::Slice(type_ref) => go(type_ref, f),
                 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
                     for bound in bounds {
-                        match bound {
-                            TypeBound::Path(path) => go_path(path, f),
+                        match bound.as_ref() {
+                            TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
+                                go_path(path, f)
+                            }
                             TypeBound::Lifetime(_) | TypeBound::Error => (),
                         }
                     }
@@ -252,8 +286,10 @@ fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
                             go(type_ref, f);
                         }
                         for bound in &binding.bounds {
-                            match bound {
-                                TypeBound::Path(path) => go_path(path, f),
+                            match bound.as_ref() {
+                                TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
+                                    go_path(path, f)
+                                }
                                 TypeBound::Lifetime(_) | TypeBound::Error => (),
                             }
                         }
@@ -267,9 +303,9 @@ fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
 pub(crate) fn type_bounds_from_ast(
     lower_ctx: &LowerCtx,
     type_bounds_opt: Option<ast::TypeBoundList>,
-) -> Vec<TypeBound> {
+) -> Vec<Interned<TypeBound>> {
     if let Some(type_bounds) = type_bounds_opt {
-        type_bounds.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
+        type_bounds.bounds().map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it))).collect()
     } else {
         vec![]
     }
@@ -277,30 +313,46 @@ pub(crate) fn type_bounds_from_ast(
 
 impl TypeBound {
     pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
+        let lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?);
+
         match node.kind() {
             ast::TypeBoundKind::PathType(path_type) => {
-                let path = match path_type.path() {
-                    Some(p) => p,
-                    None => return TypeBound::Error,
+                let m = match node.question_mark_token() {
+                    Some(_) => TraitBoundModifier::Maybe,
+                    None => TraitBoundModifier::None,
                 };
-
-                let path = match ctx.lower_path(path) {
-                    Some(p) => p,
-                    None => return TypeBound::Error,
+                lower_path_type(path_type)
+                    .map(|p| TypeBound::Path(p, m))
+                    .unwrap_or(TypeBound::Error)
+            }
+            ast::TypeBoundKind::ForType(for_type) => {
+                let lt_refs = match for_type.generic_param_list() {
+                    Some(gpl) => gpl
+                        .lifetime_params()
+                        .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(&lt)))
+                        .collect(),
+                    None => Box::default(),
                 };
-                TypeBound::Path(path)
+                let path = for_type.ty().and_then(|ty| match ty {
+                    ast::Type::PathType(path_type) => lower_path_type(path_type),
+                    _ => None,
+                });
+                match path {
+                    Some(p) => TypeBound::ForLifetime(lt_refs, p),
+                    None => TypeBound::Error,
+                }
             }
-            ast::TypeBoundKind::ForType(_) => TypeBound::Error, // FIXME ForType
             ast::TypeBoundKind::Lifetime(lifetime) => {
                 TypeBound::Lifetime(LifetimeRef::new(&lifetime))
             }
         }
     }
 
-    pub fn as_path(&self) -> Option<&Path> {
+    pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
         match self {
-            TypeBound::Path(p) => Some(p),
-            _ => None,
+            TypeBound::Path(p, m) => Some((p, m)),
+            TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
+            TypeBound::Lifetime(_) | TypeBound::Error => None,
         }
     }
 }
@@ -313,6 +365,10 @@ pub enum ConstScalar {
     Usize(u64),
 
     /// Case of an unknown value that rustc might know but we don't
+    // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
+    // constants
+    // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177
+    // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
     Unknown,
 }
 
@@ -326,6 +382,16 @@ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error>
 }
 
 impl ConstScalar {
+    /// Gets a target usize out of the ConstScalar
+    pub fn as_usize(&self) -> Option<u64> {
+        match self {
+            &ConstScalar::Usize(us) => Some(us),
+            _ => None,
+        }
+    }
+
+    // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
+    // parse stage.
     fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
         match expr {
             ast::Expr::Literal(lit) => {