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