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