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.
8 use std::convert::TryInto;
9 use syntax::ast::{self, HasName};
11 use crate::{body::LowerCtx, intern::Interned, path::Path};
13 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
20 pub fn from_mutable(mutable: bool) -> Mutability {
28 pub fn as_keyword_for_ref(self) -> &'static str {
30 Mutability::Shared => "",
31 Mutability::Mut => "mut ",
35 pub fn as_keyword_for_ptr(self) -> &'static str {
37 Mutability::Shared => "const ",
38 Mutability::Mut => "mut ",
43 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
50 pub fn from_raw(is_raw: bool) -> Rawness {
59 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
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`
69 ast::Type::PathType(path) => {
70 path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
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)]
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),
94 /// A fn pointer. Last element of the vector is the return type.
95 Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/),
97 ImplTrait(Vec<Interned<TypeBound>>),
98 DynTrait(Vec<Interned<TypeBound>>),
99 Macro(AstId<ast::MacroCall>),
103 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
104 pub struct LifetimeRef {
109 pub(crate) fn new_name(name: Name) -> Self {
113 pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
114 LifetimeRef { name: Name::new_lifetime(lifetime) }
117 pub fn missing() -> LifetimeRef {
118 LifetimeRef { name: Name::missing() }
122 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
124 Path(Path, TraitBoundModifier),
125 ForLifetime(Box<[Name]>, Path),
126 Lifetime(LifetimeRef),
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 {
139 /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
140 pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
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())
146 ast::Type::NeverType(..) => TypeRef::Never,
147 ast::Type::PathType(inner) => {
148 // FIXME: Use `Path::from_src`
151 .and_then(|it| ctx.lower_path(it))
153 .unwrap_or(TypeRef::Error)
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)
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];
167 .map(ConstScalar::usize_from_literal_expr)
168 .unwrap_or(ConstScalar::Unknown);
170 TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
172 ast::Type::SliceType(inner) => {
173 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
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(<));
178 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
179 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
181 ast::Type::InferType(_inner) => TypeRef::Placeholder,
182 ast::Type::FnPtrType(inner) => {
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();
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),
209 params.push((None, ret_ty));
210 TypeRef::Fn(params, is_varargs)
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()))
217 ast::Type::DynTraitType(inner) => {
218 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
220 ast::Type::MacroType(mt) => match mt.macro_call() {
223 .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
224 .unwrap_or(TypeRef::Error),
225 None => TypeRef::Error,
230 pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
232 Some(node) => TypeRef::from_ast(ctx, node),
233 None => TypeRef::Error,
237 pub(crate) fn unit() -> TypeRef {
238 TypeRef::Tuple(Vec::new())
241 pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
244 fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
247 TypeRef::Fn(params, _) => {
248 params.iter().for_each(|(_, param_type)| go(¶m_type, f))
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) => {
261 TypeBound::Lifetime(_) | TypeBound::Error => (),
265 TypeRef::Path(path) => go_path(path, f),
266 TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
270 fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
271 if let Some(type_ref) = path.type_anchor() {
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 {
278 crate::path::GenericArg::Type(type_ref) => {
281 crate::path::GenericArg::Lifetime(_) => {}
284 for binding in &args_and_bindings.bindings {
285 if let Some(type_ref) = &binding.type_ref {
288 for bound in &binding.bounds {
289 match bound.as_ref() {
290 TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
293 TypeBound::Lifetime(_) | TypeBound::Error => (),
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()
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()?);
319 ast::TypeBoundKind::PathType(path_type) => {
320 let m = match node.question_mark_token() {
321 Some(_) => TraitBoundModifier::Maybe,
322 None => TraitBoundModifier::None,
324 lower_path_type(path_type)
325 .map(|p| TypeBound::Path(p, m))
326 .unwrap_or(TypeBound::Error)
328 ast::TypeBoundKind::ForType(for_type) => {
329 let lt_refs = match for_type.generic_param_list() {
332 .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(<)))
334 None => Box::default(),
336 let path = for_type.ty().and_then(|ty| match ty {
337 ast::Type::PathType(path_type) => lower_path_type(path_type),
341 Some(p) => TypeBound::ForLifetime(lt_refs, p),
342 None => TypeBound::Error,
345 ast::TypeBoundKind::Lifetime(lifetime) => {
346 TypeBound::Lifetime(LifetimeRef::new(&lifetime))
351 pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
353 TypeBound::Path(p, m) => Some((p, m)),
354 TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
355 TypeBound::Lifetime(_) | TypeBound::Error => None,
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
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
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
375 impl std::fmt::Display for ConstScalar {
376 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
378 ConstScalar::Usize(us) => write!(fmt, "{}", us),
379 ConstScalar::Unknown => write!(fmt, "_"),
385 /// Gets a target usize out of the ConstScalar
386 pub fn as_usize(&self) -> Option<u64> {
388 &ConstScalar::Usize(us) => Some(us),
393 // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
395 fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
397 ast::Expr::Literal(lit) => {
398 let lkind = lit.kind();
400 ast::LiteralKind::IntNumber(num)
401 if num.suffix() == None || num.suffix() == Some("usize") =>
403 num.value().and_then(|v| v.try_into().ok())
410 .map(ConstScalar::Usize)
411 .unwrap_or(ConstScalar::Unknown)