]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/type_ref.rs
feat: support concat_bytes
[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::{
5     name::{AsName, Name},
6     AstId, InFile,
7 };
8 use std::convert::TryInto;
9 use syntax::ast::{self, HasName};
10
11 use crate::{body::LowerCtx, intern::Interned, path::Path};
12
13 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
14 pub enum Mutability {
15     Shared,
16     Mut,
17 }
18
19 impl Mutability {
20     pub fn from_mutable(mutable: bool) -> Mutability {
21         if mutable {
22             Mutability::Mut
23         } else {
24             Mutability::Shared
25         }
26     }
27
28     pub fn as_keyword_for_ref(self) -> &'static str {
29         match self {
30             Mutability::Shared => "",
31             Mutability::Mut => "mut ",
32         }
33     }
34
35     pub fn as_keyword_for_ptr(self) -> &'static str {
36         match self {
37             Mutability::Shared => "const ",
38             Mutability::Mut => "mut ",
39         }
40     }
41 }
42
43 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
44 pub enum Rawness {
45     RawPtr,
46     Ref,
47 }
48
49 impl Rawness {
50     pub fn from_raw(is_raw: bool) -> Rawness {
51         if is_raw {
52             Rawness::RawPtr
53         } else {
54             Rawness::Ref
55         }
56     }
57 }
58
59 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
60 pub struct TraitRef {
61     pub path: Path,
62 }
63
64 impl TraitRef {
65     /// Converts an `ast::PathType` to a `hir::TraitRef`.
66     pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
67         // FIXME: Use `Path::from_src`
68         match node {
69             ast::Type::PathType(path) => {
70                 path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
71             }
72             _ => None,
73         }
74     }
75 }
76
77 /// Compare ty::Ty
78 ///
79 /// Note: Most users of `TypeRef` that end up in the salsa database intern it using
80 /// `Interned<TypeRef>` to save space. But notably, nested `TypeRef`s are not interned, since that
81 /// does not seem to save any noticeable amount of memory.
82 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
83 pub enum TypeRef {
84     Never,
85     Placeholder,
86     Tuple(Vec<TypeRef>),
87     Path(Path),
88     RawPtr(Box<TypeRef>, Mutability),
89     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
90     // FIXME: for full const generics, the latter element (length) here is going to have to be an
91     // expression that is further lowered later in hir_ty.
92     Array(Box<TypeRef>, ConstScalar),
93     Slice(Box<TypeRef>),
94     /// A fn pointer. Last element of the vector is the return type.
95     Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/),
96     // For
97     ImplTrait(Vec<Interned<TypeBound>>),
98     DynTrait(Vec<Interned<TypeBound>>),
99     Macro(AstId<ast::MacroCall>),
100     Error,
101 }
102
103 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
104 pub struct LifetimeRef {
105     pub name: Name,
106 }
107
108 impl LifetimeRef {
109     pub(crate) fn new_name(name: Name) -> Self {
110         LifetimeRef { name }
111     }
112
113     pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
114         LifetimeRef { name: Name::new_lifetime(lifetime) }
115     }
116
117     pub fn missing() -> LifetimeRef {
118         LifetimeRef { name: Name::missing() }
119     }
120 }
121
122 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
123 pub enum TypeBound {
124     Path(Path, TraitBoundModifier),
125     ForLifetime(Box<[Name]>, Path),
126     Lifetime(LifetimeRef),
127     Error,
128 }
129
130 /// A modifier on a bound, currently this is only used for `?Sized`, where the
131 /// modifier is `Maybe`.
132 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
133 pub enum TraitBoundModifier {
134     None,
135     Maybe,
136 }
137
138 impl TypeRef {
139     /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
140     pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
141         match node {
142             ast::Type::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
143             ast::Type::TupleType(inner) => {
144                 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
145             }
146             ast::Type::NeverType(..) => TypeRef::Never,
147             ast::Type::PathType(inner) => {
148                 // FIXME: Use `Path::from_src`
149                 inner
150                     .path()
151                     .and_then(|it| ctx.lower_path(it))
152                     .map(TypeRef::Path)
153                     .unwrap_or(TypeRef::Error)
154             }
155             ast::Type::PtrType(inner) => {
156                 let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
157                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
158                 TypeRef::RawPtr(Box::new(inner_ty), mutability)
159             }
160             ast::Type::ArrayType(inner) => {
161                 // FIXME: This is a hack. We should probably reuse the machinery of
162                 // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
163                 // `hir_ty` level, which would allow knowing the type of:
164                 // let v: [u8; 2 + 2] = [0u8; 4];
165                 let len = inner
166                     .expr()
167                     .map(ConstScalar::usize_from_literal_expr)
168                     .unwrap_or(ConstScalar::Unknown);
169
170                 TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
171             }
172             ast::Type::SliceType(inner) => {
173                 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
174             }
175             ast::Type::RefType(inner) => {
176                 let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
177                 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
178                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
179                 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
180             }
181             ast::Type::InferType(_inner) => TypeRef::Placeholder,
182             ast::Type::FnPtrType(inner) => {
183                 let ret_ty = inner
184                     .ret_type()
185                     .and_then(|rt| rt.ty())
186                     .map(|it| TypeRef::from_ast(ctx, it))
187                     .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
188                 let mut is_varargs = false;
189                 let mut params = if let Some(pl) = inner.param_list() {
190                     if let Some(param) = pl.params().last() {
191                         is_varargs = param.dotdotdot_token().is_some();
192                     }
193
194                     pl.params()
195                         .map(|it| {
196                             let type_ref = TypeRef::from_ast_opt(ctx, it.ty());
197                             let name = match it.pat() {
198                                 Some(ast::Pat::IdentPat(it)) => Some(
199                                     it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing),
200                                 ),
201                                 _ => None,
202                             };
203                             (name, type_ref)
204                         })
205                         .collect()
206                 } else {
207                     Vec::new()
208                 };
209                 params.push((None, ret_ty));
210                 TypeRef::Fn(params, is_varargs)
211             }
212             // for types are close enough for our purposes to the inner type for now...
213             ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
214             ast::Type::ImplTraitType(inner) => {
215                 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
216             }
217             ast::Type::DynTraitType(inner) => {
218                 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
219             }
220             ast::Type::MacroType(mt) => match mt.macro_call() {
221                 Some(mc) => ctx
222                     .ast_id(&mc)
223                     .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
224                     .unwrap_or(TypeRef::Error),
225                 None => TypeRef::Error,
226             },
227         }
228     }
229
230     pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
231         match node {
232             Some(node) => TypeRef::from_ast(ctx, node),
233             None => TypeRef::Error,
234         }
235     }
236
237     pub(crate) fn unit() -> TypeRef {
238         TypeRef::Tuple(Vec::new())
239     }
240
241     pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
242         go(self, f);
243
244         fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
245             f(type_ref);
246             match type_ref {
247                 TypeRef::Fn(params, _) => {
248                     params.iter().for_each(|(_, param_type)| go(&param_type, f))
249                 }
250                 TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
251                 TypeRef::RawPtr(type_ref, _)
252                 | TypeRef::Reference(type_ref, ..)
253                 | TypeRef::Array(type_ref, _)
254                 | TypeRef::Slice(type_ref) => go(type_ref, f),
255                 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
256                     for bound in bounds {
257                         match bound.as_ref() {
258                             TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
259                                 go_path(path, f)
260                             }
261                             TypeBound::Lifetime(_) | TypeBound::Error => (),
262                         }
263                     }
264                 }
265                 TypeRef::Path(path) => go_path(path, f),
266                 TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
267             };
268         }
269
270         fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
271             if let Some(type_ref) = path.type_anchor() {
272                 go(type_ref, f);
273             }
274             for segment in path.segments().iter() {
275                 if let Some(args_and_bindings) = segment.args_and_bindings {
276                     for arg in &args_and_bindings.args {
277                         match arg {
278                             crate::path::GenericArg::Type(type_ref) => {
279                                 go(type_ref, f);
280                             }
281                             crate::path::GenericArg::Lifetime(_) => {}
282                         }
283                     }
284                     for binding in &args_and_bindings.bindings {
285                         if let Some(type_ref) = &binding.type_ref {
286                             go(type_ref, f);
287                         }
288                         for bound in &binding.bounds {
289                             match bound.as_ref() {
290                                 TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
291                                     go_path(path, f)
292                                 }
293                                 TypeBound::Lifetime(_) | TypeBound::Error => (),
294                             }
295                         }
296                     }
297                 }
298             }
299         }
300     }
301 }
302
303 pub(crate) fn type_bounds_from_ast(
304     lower_ctx: &LowerCtx,
305     type_bounds_opt: Option<ast::TypeBoundList>,
306 ) -> Vec<Interned<TypeBound>> {
307     if let Some(type_bounds) = type_bounds_opt {
308         type_bounds.bounds().map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it))).collect()
309     } else {
310         vec![]
311     }
312 }
313
314 impl TypeBound {
315     pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
316         let lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?);
317
318         match node.kind() {
319             ast::TypeBoundKind::PathType(path_type) => {
320                 let m = match node.question_mark_token() {
321                     Some(_) => TraitBoundModifier::Maybe,
322                     None => TraitBoundModifier::None,
323                 };
324                 lower_path_type(path_type)
325                     .map(|p| TypeBound::Path(p, m))
326                     .unwrap_or(TypeBound::Error)
327             }
328             ast::TypeBoundKind::ForType(for_type) => {
329                 let lt_refs = match for_type.generic_param_list() {
330                     Some(gpl) => gpl
331                         .lifetime_params()
332                         .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(&lt)))
333                         .collect(),
334                     None => Box::default(),
335                 };
336                 let path = for_type.ty().and_then(|ty| match ty {
337                     ast::Type::PathType(path_type) => lower_path_type(path_type),
338                     _ => None,
339                 });
340                 match path {
341                     Some(p) => TypeBound::ForLifetime(lt_refs, p),
342                     None => TypeBound::Error,
343                 }
344             }
345             ast::TypeBoundKind::Lifetime(lifetime) => {
346                 TypeBound::Lifetime(LifetimeRef::new(&lifetime))
347             }
348         }
349     }
350
351     pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
352         match self {
353             TypeBound::Path(p, m) => Some((p, m)),
354             TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
355             TypeBound::Lifetime(_) | TypeBound::Error => None,
356         }
357     }
358 }
359
360 /// A concrete constant value
361 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
362 pub enum ConstScalar {
363     // for now, we only support the trivial case of constant evaluating the length of an array
364     // Note that this is u64 because the target usize may be bigger than our usize
365     Usize(u64),
366
367     /// Case of an unknown value that rustc might know but we don't
368     // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
369     // constants
370     // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177
371     // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
372     Unknown,
373 }
374
375 impl std::fmt::Display for ConstScalar {
376     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
377         match self {
378             ConstScalar::Usize(us) => write!(fmt, "{}", us),
379             ConstScalar::Unknown => write!(fmt, "_"),
380         }
381     }
382 }
383
384 impl ConstScalar {
385     /// Gets a target usize out of the ConstScalar
386     pub fn as_usize(&self) -> Option<u64> {
387         match self {
388             &ConstScalar::Usize(us) => Some(us),
389             _ => None,
390         }
391     }
392
393     // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
394     // parse stage.
395     fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
396         match expr {
397             ast::Expr::Literal(lit) => {
398                 let lkind = lit.kind();
399                 match lkind {
400                     ast::LiteralKind::IntNumber(num)
401                         if num.suffix() == None || num.suffix() == Some("usize") =>
402                     {
403                         num.value().and_then(|v| v.try_into().ok())
404                     }
405                     _ => None,
406                 }
407             }
408             _ => None,
409         }
410         .map(ConstScalar::Usize)
411         .unwrap_or(ConstScalar::Unknown)
412     }
413 }