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(<));
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(<)))
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