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