]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/type_ref.rs
Add lowering of array lengths in types
[rust.git] / crates / hir_def / src / type_ref.rs
1 //! HIR for references to types. Paths in these are not yet resolved. They can
2 //! be directly created from an ast::TypeRef, without further queries.
3
4 use hir_expand::{name::Name, AstId, InFile};
5 use std::convert::TryInto;
6 use syntax::ast;
7
8 use crate::{body::LowerCtx, path::Path};
9
10 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
11 pub enum Mutability {
12     Shared,
13     Mut,
14 }
15
16 impl Mutability {
17     pub fn from_mutable(mutable: bool) -> Mutability {
18         if mutable {
19             Mutability::Mut
20         } else {
21             Mutability::Shared
22         }
23     }
24
25     pub fn as_keyword_for_ref(self) -> &'static str {
26         match self {
27             Mutability::Shared => "",
28             Mutability::Mut => "mut ",
29         }
30     }
31
32     pub fn as_keyword_for_ptr(self) -> &'static str {
33         match self {
34             Mutability::Shared => "const ",
35             Mutability::Mut => "mut ",
36         }
37     }
38 }
39
40 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
41 pub enum Rawness {
42     RawPtr,
43     Ref,
44 }
45
46 impl Rawness {
47     pub fn from_raw(is_raw: bool) -> Rawness {
48         if is_raw {
49             Rawness::RawPtr
50         } else {
51             Rawness::Ref
52         }
53     }
54 }
55
56 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
57 pub struct TraitRef {
58     pub path: Path,
59 }
60
61 impl TraitRef {
62     /// Converts an `ast::PathType` to a `hir::TraitRef`.
63     pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
64         // FIXME: Use `Path::from_src`
65         match node {
66             ast::Type::PathType(path) => {
67                 path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
68             }
69             _ => None,
70         }
71     }
72 }
73
74 /// Compare ty::Ty
75 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
76 pub enum TypeRef {
77     Never,
78     Placeholder,
79     Tuple(Vec<TypeRef>),
80     Path(Path),
81     RawPtr(Box<TypeRef>, Mutability),
82     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
83     Array(Box<TypeRef>, ConstScalar),
84     Slice(Box<TypeRef>),
85     /// A fn pointer. Last element of the vector is the return type.
86     Fn(Vec<TypeRef>, bool /*varargs*/),
87     // For
88     ImplTrait(Vec<TypeBound>),
89     DynTrait(Vec<TypeBound>),
90     Macro(AstId<ast::MacroCall>),
91     Error,
92 }
93
94 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
95 pub struct LifetimeRef {
96     pub name: Name,
97 }
98
99 impl LifetimeRef {
100     pub(crate) fn new_name(name: Name) -> Self {
101         LifetimeRef { name }
102     }
103
104     pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
105         LifetimeRef { name: Name::new_lifetime(lifetime) }
106     }
107
108     pub fn missing() -> LifetimeRef {
109         LifetimeRef { name: Name::missing() }
110     }
111 }
112
113 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
114 pub enum TypeBound {
115     Path(Path),
116     // ForLifetime(Vec<LifetimeRef>, Path), FIXME ForLifetime
117     Lifetime(LifetimeRef),
118     Error,
119 }
120
121 impl TypeRef {
122     /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
123     pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
124         match node {
125             ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
126             ast::Type::TupleType(inner) => {
127                 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
128             }
129             ast::Type::NeverType(..) => TypeRef::Never,
130             ast::Type::PathType(inner) => {
131                 // FIXME: Use `Path::from_src`
132                 inner
133                     .path()
134                     .and_then(|it| ctx.lower_path(it))
135                     .map(TypeRef::Path)
136                     .unwrap_or(TypeRef::Error)
137             }
138             ast::Type::PtrType(inner) => {
139                 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
140                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
141                 TypeRef::RawPtr(Box::new(inner_ty), mutability)
142             }
143             ast::Type::ArrayType(inner) => {
144                 let len = inner
145                     .expr()
146                     .map(ConstScalar::usize_from_literal_expr)
147                     .unwrap_or(ConstScalar::Unknown);
148
149                 TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len)
150             }
151             ast::Type::SliceType(inner) => {
152                 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
153             }
154             ast::Type::RefType(inner) => {
155                 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
156                 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
157                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
158                 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
159             }
160             ast::Type::InferType(_inner) => TypeRef::Placeholder,
161             ast::Type::FnPtrType(inner) => {
162                 let ret_ty = inner
163                     .ret_type()
164                     .and_then(|rt| rt.ty())
165                     .map(|it| TypeRef::from_ast(ctx, it))
166                     .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
167                 let mut is_varargs = false;
168                 let mut params = if let Some(pl) = inner.param_list() {
169                     if let Some(param) = pl.params().last() {
170                         is_varargs = param.dotdotdot_token().is_some();
171                     }
172
173                     pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect()
174                 } else {
175                     Vec::new()
176                 };
177                 params.push(ret_ty);
178                 TypeRef::Fn(params, is_varargs)
179             }
180             // for types are close enough for our purposes to the inner type for now...
181             ast::Type::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
182             ast::Type::ImplTraitType(inner) => {
183                 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
184             }
185             ast::Type::DynTraitType(inner) => {
186                 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
187             }
188             ast::Type::MacroType(mt) => match mt.macro_call() {
189                 Some(mc) => ctx
190                     .ast_id(&mc)
191                     .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
192                     .unwrap_or(TypeRef::Error),
193                 None => TypeRef::Error,
194             },
195         }
196     }
197
198     pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
199         if let Some(node) = node {
200             TypeRef::from_ast(ctx, node)
201         } else {
202             TypeRef::Error
203         }
204     }
205
206     pub(crate) fn unit() -> TypeRef {
207         TypeRef::Tuple(Vec::new())
208     }
209
210     pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
211         go(self, f);
212
213         fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
214             f(type_ref);
215             match type_ref {
216                 TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
217                     types.iter().for_each(|t| go(t, f))
218                 }
219                 TypeRef::RawPtr(type_ref, _)
220                 | TypeRef::Reference(type_ref, ..)
221                 | TypeRef::Array(type_ref, _)
222                 | TypeRef::Slice(type_ref) => go(&type_ref, f),
223                 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
224                     for bound in bounds {
225                         match bound {
226                             TypeBound::Path(path) => go_path(path, f),
227                             TypeBound::Lifetime(_) | TypeBound::Error => (),
228                         }
229                     }
230                 }
231                 TypeRef::Path(path) => go_path(path, f),
232                 TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
233             };
234         }
235
236         fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
237             if let Some(type_ref) = path.type_anchor() {
238                 go(type_ref, f);
239             }
240             for segment in path.segments().iter() {
241                 if let Some(args_and_bindings) = segment.args_and_bindings {
242                     for arg in &args_and_bindings.args {
243                         match arg {
244                             crate::path::GenericArg::Type(type_ref) => {
245                                 go(type_ref, f);
246                             }
247                             crate::path::GenericArg::Lifetime(_) => {}
248                         }
249                     }
250                     for binding in &args_and_bindings.bindings {
251                         if let Some(type_ref) = &binding.type_ref {
252                             go(type_ref, f);
253                         }
254                         for bound in &binding.bounds {
255                             match bound {
256                                 TypeBound::Path(path) => go_path(path, f),
257                                 TypeBound::Lifetime(_) | TypeBound::Error => (),
258                             }
259                         }
260                     }
261                 }
262             }
263         }
264     }
265 }
266
267 pub(crate) fn type_bounds_from_ast(
268     lower_ctx: &LowerCtx,
269     type_bounds_opt: Option<ast::TypeBoundList>,
270 ) -> Vec<TypeBound> {
271     if let Some(type_bounds) = type_bounds_opt {
272         type_bounds.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
273     } else {
274         vec![]
275     }
276 }
277
278 impl TypeBound {
279     pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
280         match node.kind() {
281             ast::TypeBoundKind::PathType(path_type) => {
282                 let path = match path_type.path() {
283                     Some(p) => p,
284                     None => return TypeBound::Error,
285                 };
286
287                 let path = match ctx.lower_path(path) {
288                     Some(p) => p,
289                     None => return TypeBound::Error,
290                 };
291                 TypeBound::Path(path)
292             }
293             ast::TypeBoundKind::ForType(_) => TypeBound::Error, // FIXME ForType
294             ast::TypeBoundKind::Lifetime(lifetime) => {
295                 TypeBound::Lifetime(LifetimeRef::new(&lifetime))
296             }
297         }
298     }
299
300     pub fn as_path(&self) -> Option<&Path> {
301         match self {
302             TypeBound::Path(p) => Some(p),
303             _ => None,
304         }
305     }
306 }
307
308 /// A concrete constant value
309 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
310 pub enum ConstScalar {
311     // for now, we only support the trivial case of constant evaluating the length of an array
312     // Note that this is u64 because the target usize may be bigger than our usize
313     Usize(u64),
314
315     /// Case of an unknown value that rustc might know but we don't
316     Unknown,
317 }
318
319 impl std::fmt::Display for ConstScalar {
320     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
321         match self {
322             ConstScalar::Usize(us) => write!(fmt, "{}", us),
323             ConstScalar::Unknown => write!(fmt, "_"),
324         }
325     }
326 }
327
328 impl ConstScalar {
329     fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
330         match expr {
331             ast::Expr::Literal(lit) => {
332                 let lkind = lit.kind();
333                 match lkind {
334                     ast::LiteralKind::IntNumber(num)
335                         if num.suffix() == None || num.suffix() == Some("usize") =>
336                     {
337                         num.value().and_then(|v| v.try_into().ok())
338                     }
339                     _ => None,
340                 }
341             }
342             _ => None,
343         }
344         .map(ConstScalar::Usize)
345         .unwrap_or(ConstScalar::Unknown)
346     }
347 }