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.
4 use hir_expand::{name::Name, AstId, InFile};
5 use std::convert::TryInto;
8 use crate::{body::LowerCtx, path::Path};
10 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
17 pub fn from_mutable(mutable: bool) -> Mutability {
25 pub fn as_keyword_for_ref(self) -> &'static str {
27 Mutability::Shared => "",
28 Mutability::Mut => "mut ",
32 pub fn as_keyword_for_ptr(self) -> &'static str {
34 Mutability::Shared => "const ",
35 Mutability::Mut => "mut ",
40 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
47 pub fn from_raw(is_raw: bool) -> Rawness {
56 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
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`
66 ast::Type::PathType(path) => {
67 path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
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)]
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),
91 /// A fn pointer. Last element of the vector is the return type.
92 Fn(Vec<TypeRef>, bool /*varargs*/),
94 ImplTrait(Vec<TypeBound>),
95 DynTrait(Vec<TypeBound>),
96 Macro(AstId<ast::MacroCall>),
100 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
101 pub struct LifetimeRef {
106 pub(crate) fn new_name(name: Name) -> Self {
110 pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
111 LifetimeRef { name: Name::new_lifetime(lifetime) }
114 pub fn missing() -> LifetimeRef {
115 LifetimeRef { name: Name::missing() }
119 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
122 // ForLifetime(Vec<LifetimeRef>, Path), FIXME ForLifetime
123 Lifetime(LifetimeRef),
128 /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
129 pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
131 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
132 ast::Type::TupleType(inner) => {
133 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
135 ast::Type::NeverType(..) => TypeRef::Never,
136 ast::Type::PathType(inner) => {
137 // FIXME: Use `Path::from_src`
140 .and_then(|it| ctx.lower_path(it))
142 .unwrap_or(TypeRef::Error)
144 ast::Type::PtrType(inner) => {
145 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
146 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
147 TypeRef::RawPtr(Box::new(inner_ty), mutability)
149 ast::Type::ArrayType(inner) => {
150 // FIXME: This is a hack. We should probably reuse the machinery of
151 // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
152 // `hir_ty` level, which would allow knowing the type of:
153 // let v: [u8; 2 + 2] = [0u8; 4];
156 .map(ConstScalar::usize_from_literal_expr)
157 .unwrap_or(ConstScalar::Unknown);
159 TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len)
161 ast::Type::SliceType(inner) => {
162 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
164 ast::Type::RefType(inner) => {
165 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
166 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(<));
167 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
168 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
170 ast::Type::InferType(_inner) => TypeRef::Placeholder,
171 ast::Type::FnPtrType(inner) => {
174 .and_then(|rt| rt.ty())
175 .map(|it| TypeRef::from_ast(ctx, it))
176 .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
177 let mut is_varargs = false;
178 let mut params = if let Some(pl) = inner.param_list() {
179 if let Some(param) = pl.params().last() {
180 is_varargs = param.dotdotdot_token().is_some();
183 pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect()
188 TypeRef::Fn(params, is_varargs)
190 // for types are close enough for our purposes to the inner type for now...
191 ast::Type::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
192 ast::Type::ImplTraitType(inner) => {
193 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
195 ast::Type::DynTraitType(inner) => {
196 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
198 ast::Type::MacroType(mt) => match mt.macro_call() {
201 .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
202 .unwrap_or(TypeRef::Error),
203 None => TypeRef::Error,
208 pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
209 if let Some(node) = node {
210 TypeRef::from_ast(ctx, node)
216 pub(crate) fn unit() -> TypeRef {
217 TypeRef::Tuple(Vec::new())
220 pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
223 fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
226 TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
227 types.iter().for_each(|t| go(t, f))
229 TypeRef::RawPtr(type_ref, _)
230 | TypeRef::Reference(type_ref, ..)
231 | TypeRef::Array(type_ref, _)
232 | TypeRef::Slice(type_ref) => go(&type_ref, f),
233 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
234 for bound in bounds {
236 TypeBound::Path(path) => go_path(path, f),
237 TypeBound::Lifetime(_) | TypeBound::Error => (),
241 TypeRef::Path(path) => go_path(path, f),
242 TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
246 fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
247 if let Some(type_ref) = path.type_anchor() {
250 for segment in path.segments().iter() {
251 if let Some(args_and_bindings) = segment.args_and_bindings {
252 for arg in &args_and_bindings.args {
254 crate::path::GenericArg::Type(type_ref) => {
257 crate::path::GenericArg::Lifetime(_) => {}
260 for binding in &args_and_bindings.bindings {
261 if let Some(type_ref) = &binding.type_ref {
264 for bound in &binding.bounds {
266 TypeBound::Path(path) => go_path(path, f),
267 TypeBound::Lifetime(_) | TypeBound::Error => (),
277 pub(crate) fn type_bounds_from_ast(
278 lower_ctx: &LowerCtx,
279 type_bounds_opt: Option<ast::TypeBoundList>,
280 ) -> Vec<TypeBound> {
281 if let Some(type_bounds) = type_bounds_opt {
282 type_bounds.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
289 pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
291 ast::TypeBoundKind::PathType(path_type) => {
292 let path = match path_type.path() {
294 None => return TypeBound::Error,
297 let path = match ctx.lower_path(path) {
299 None => return TypeBound::Error,
301 TypeBound::Path(path)
303 ast::TypeBoundKind::ForType(_) => TypeBound::Error, // FIXME ForType
304 ast::TypeBoundKind::Lifetime(lifetime) => {
305 TypeBound::Lifetime(LifetimeRef::new(&lifetime))
310 pub fn as_path(&self) -> Option<&Path> {
312 TypeBound::Path(p) => Some(p),
318 /// A concrete constant value
319 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
320 pub enum ConstScalar {
321 // for now, we only support the trivial case of constant evaluating the length of an array
322 // Note that this is u64 because the target usize may be bigger than our usize
325 /// Case of an unknown value that rustc might know but we don't
326 // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
328 // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177
329 // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
333 impl std::fmt::Display for ConstScalar {
334 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
336 ConstScalar::Usize(us) => write!(fmt, "{}", us),
337 ConstScalar::Unknown => write!(fmt, "_"),
343 /// Gets a target usize out of the ConstScalar
344 pub fn as_usize(&self) -> Option<u64> {
346 &ConstScalar::Usize(us) => Some(us),
351 // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
353 fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
355 ast::Expr::Literal(lit) => {
356 let lkind = lit.kind();
358 ast::LiteralKind::IntNumber(num)
359 if num.suffix() == None || num.suffix() == Some("usize") =>
361 num.value().and_then(|v| v.try_into().ok())
368 .map(ConstScalar::Usize)
369 .unwrap_or(ConstScalar::Unknown)