mun_hir/ty/
lower.rs

1//! Methods for lower the HIR to types.
2
3pub(crate) use self::diagnostics::LowerDiagnostic;
4use crate::resolve::{HasResolver, TypeNs};
5use crate::ty::{Substitution, TyKind};
6use crate::{
7    arena::map::ArenaMap,
8    code_model::StructKind,
9    diagnostics::DiagnosticSink,
10    name_resolution::Namespace,
11    primitive_type::PrimitiveType,
12    resolve::Resolver,
13    ty::{FnSig, Ty},
14    type_ref::{LocalTypeRefId, TypeRef, TypeRefMap, TypeRefSourceMap},
15    FileId, Function, HirDatabase, ModuleDef, Path, Struct, TypeAlias,
16};
17use crate::{HasVisibility, Visibility};
18use std::{ops::Index, sync::Arc};
19
20/// A struct which holds resolved type references to `Ty`s.
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct LowerTyMap {
23    pub(crate) type_ref_to_type: ArenaMap<LocalTypeRefId, Ty>,
24    pub(crate) diagnostics: Vec<LowerDiagnostic>,
25
26    unknown_ty: Ty,
27}
28
29impl Default for LowerTyMap {
30    fn default() -> Self {
31        LowerTyMap {
32            type_ref_to_type: Default::default(),
33            diagnostics: vec![],
34            unknown_ty: TyKind::Unknown.intern(),
35        }
36    }
37}
38
39impl Index<LocalTypeRefId> for LowerTyMap {
40    type Output = Ty;
41    fn index(&self, expr: LocalTypeRefId) -> &Ty {
42        self.type_ref_to_type.get(expr).unwrap_or(&self.unknown_ty)
43    }
44}
45
46impl LowerTyMap {
47    /// Adds all the `LowerDiagnostic`s of the result to the `DiagnosticSink`.
48    pub(crate) fn add_diagnostics(
49        &self,
50        db: &dyn HirDatabase,
51        file_id: FileId,
52        source_map: &TypeRefSourceMap,
53        sink: &mut DiagnosticSink,
54    ) {
55        self.diagnostics
56            .iter()
57            .for_each(|it| it.add_to(db, file_id, source_map, sink))
58    }
59}
60
61impl Ty {
62    /// Tries to lower a HIR type reference to an actual resolved type. Besides the type also
63    /// returns an diagnostics that where encountered along the way.
64    pub(crate) fn from_hir(
65        db: &dyn HirDatabase,
66        resolver: &Resolver,
67        type_ref_map: &TypeRefMap,
68        type_ref: LocalTypeRefId,
69    ) -> (Ty, Vec<diagnostics::LowerDiagnostic>) {
70        let mut diagnostics = Vec::new();
71        let ty =
72            Ty::from_hir_with_diagnostics(db, resolver, type_ref_map, &mut diagnostics, type_ref);
73        (ty, diagnostics)
74    }
75
76    /// Tries to lower a HIR type reference to an actual resolved type. Takes a mutable reference
77    /// to a `Vec` which will hold any diagnostics encountered a long the way.
78    fn from_hir_with_diagnostics(
79        db: &dyn HirDatabase,
80        resolver: &Resolver,
81        type_ref_map: &TypeRefMap,
82        diagnostics: &mut Vec<LowerDiagnostic>,
83        type_ref: LocalTypeRefId,
84    ) -> Ty {
85        let res = match &type_ref_map[type_ref] {
86            TypeRef::Path(path) => Ty::from_path(db, resolver, type_ref, path, diagnostics),
87            TypeRef::Error => Some((TyKind::Unknown.intern(), false)),
88            TypeRef::Tuple(inner) => {
89                let inner_tys = inner.iter().map(|tr| {
90                    Self::from_hir_with_diagnostics(db, resolver, type_ref_map, diagnostics, *tr)
91                });
92                Some((
93                    TyKind::Tuple(inner_tys.len(), inner_tys.collect()).intern(),
94                    false,
95                ))
96            }
97            TypeRef::Never => Some((TyKind::Never.intern(), false)),
98            TypeRef::Array(inner) => {
99                let inner = Self::from_hir_with_diagnostics(
100                    db,
101                    resolver,
102                    type_ref_map,
103                    diagnostics,
104                    *inner,
105                );
106                Some((TyKind::Array(inner).intern(), false))
107            }
108        };
109        if let Some((ty, is_cyclic)) = res {
110            if is_cyclic {
111                diagnostics.push(LowerDiagnostic::CyclicType { id: type_ref })
112            }
113            ty
114        } else {
115            diagnostics.push(LowerDiagnostic::UnresolvedType { id: type_ref });
116            TyKind::Unknown.intern()
117        }
118    }
119
120    /// Constructs a `Ty` from a path.
121    fn from_path(
122        db: &dyn HirDatabase,
123        resolver: &Resolver,
124        type_ref: LocalTypeRefId,
125        path: &Path,
126        diagnostics: &mut Vec<LowerDiagnostic>,
127    ) -> Option<(Self, bool)> {
128        // Find the type
129        let (ty, vis) = resolver.resolve_path_as_type_fully(db.upcast(), path)?;
130
131        // Get the definition and visibility
132        let def = match ty {
133            TypeNs::StructId(id) => TypableDef::Struct(id.into()),
134            TypeNs::TypeAliasId(id) => TypableDef::TypeAlias(id.into()),
135            TypeNs::PrimitiveType(id) => TypableDef::PrimitiveType(id),
136        };
137
138        // Get the current module and see if the type is visible from here
139        if let Some(module) = resolver.module() {
140            if !vis.is_visible_from(db, module) {
141                diagnostics.push(LowerDiagnostic::TypeIsPrivate { id: type_ref })
142            }
143        }
144
145        Some(db.type_for_def(def, Namespace::Types))
146    }
147}
148
149/// Resolves all types in the specified `TypeRefMap`.
150pub fn types_from_hir(
151    db: &dyn HirDatabase,
152    resolver: &Resolver,
153    type_ref_map: &TypeRefMap,
154) -> Arc<LowerTyMap> {
155    let mut result = LowerTyMap::default();
156    for (id, _) in type_ref_map.iter() {
157        let ty =
158            Ty::from_hir_with_diagnostics(db, resolver, type_ref_map, &mut result.diagnostics, id);
159        result.type_ref_to_type.insert(id, ty);
160    }
161    Arc::new(result)
162}
163
164pub fn lower_struct_query(db: &dyn HirDatabase, s: Struct) -> Arc<LowerTyMap> {
165    let data = s.data(db.upcast());
166    types_from_hir(db, &s.id.resolver(db.upcast()), data.type_ref_map())
167}
168
169pub fn lower_type_alias_query(db: &dyn HirDatabase, t: TypeAlias) -> Arc<LowerTyMap> {
170    let data = t.data(db.upcast());
171    types_from_hir(db, &t.id.resolver(db.upcast()), data.type_ref_map())
172}
173
174#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
175pub enum TypableDef {
176    Function(Function),
177    PrimitiveType(PrimitiveType),
178    Struct(Struct),
179    TypeAlias(TypeAlias),
180}
181
182impl From<Function> for TypableDef {
183    fn from(f: Function) -> Self {
184        TypableDef::Function(f)
185    }
186}
187
188impl From<PrimitiveType> for TypableDef {
189    fn from(f: PrimitiveType) -> Self {
190        TypableDef::PrimitiveType(f)
191    }
192}
193
194impl From<Struct> for TypableDef {
195    fn from(f: Struct) -> Self {
196        TypableDef::Struct(f)
197    }
198}
199
200impl From<ModuleDef> for Option<TypableDef> {
201    fn from(d: ModuleDef) -> Self {
202        match d {
203            ModuleDef::Function(f) => Some(TypableDef::Function(f)),
204            ModuleDef::PrimitiveType(t) => Some(TypableDef::PrimitiveType(t)),
205            ModuleDef::Struct(t) => Some(TypableDef::Struct(t)),
206            ModuleDef::TypeAlias(t) => Some(TypableDef::TypeAlias(t)),
207            ModuleDef::Module(_) => None,
208        }
209    }
210}
211
212#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
213pub enum CallableDef {
214    Function(Function),
215    Struct(Struct),
216}
217impl_froms!(CallableDef: Function, Struct);
218
219impl CallableDef {
220    pub fn is_function(self) -> bool {
221        matches!(self, CallableDef::Function(_))
222    }
223
224    pub fn is_struct(self) -> bool {
225        matches!(self, CallableDef::Struct(_))
226    }
227}
228
229impl HasVisibility for CallableDef {
230    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
231        match self {
232            CallableDef::Struct(strukt) => strukt.visibility(db),
233            CallableDef::Function(function) => function.visibility(db),
234        }
235    }
236}
237
238/// Build the declared type of an item. This depends on the namespace; e.g. for
239/// `struct Foo(usize)`, we have two types: The type of the struct itself, and
240/// the constructor function `(usize) -> Foo` which lives in the values
241/// namespace.
242pub(crate) fn type_for_def(db: &dyn HirDatabase, def: TypableDef, ns: Namespace) -> (Ty, bool) {
243    let ty = match (def, ns) {
244        (TypableDef::Function(f), Namespace::Values) => type_for_fn(db, f),
245        (TypableDef::PrimitiveType(t), Namespace::Types) => type_for_primitive(t),
246        (TypableDef::Struct(s), Namespace::Values) => type_for_struct_constructor(db, s),
247        (TypableDef::Struct(s), Namespace::Types) => type_for_struct(db, s),
248        (TypableDef::TypeAlias(t), Namespace::Types) => type_for_type_alias(db, t),
249
250        // 'error' cases:
251        (TypableDef::Function(_), Namespace::Types) => TyKind::Unknown.intern(),
252        (TypableDef::PrimitiveType(_), Namespace::Values) => TyKind::Unknown.intern(),
253        (TypableDef::TypeAlias(_), Namespace::Values) => TyKind::Unknown.intern(),
254    };
255    (ty, false)
256}
257
258/// Recover with an unknown type when a cycle is detected in the salsa database.
259pub(crate) fn type_for_cycle_recover(
260    _db: &dyn HirDatabase,
261    _cycle: &[String],
262    _def: &TypableDef,
263    _ns: &Namespace,
264) -> (Ty, bool) {
265    (TyKind::Unknown.intern(), true)
266}
267
268/// Build the declared type of a static.
269fn type_for_primitive(def: PrimitiveType) -> Ty {
270    match def {
271        PrimitiveType::Float(f) => TyKind::Float(f.into()),
272        PrimitiveType::Int(i) => TyKind::Int(i.into()),
273        PrimitiveType::Bool => TyKind::Bool,
274    }
275    .intern()
276}
277
278/// Build the declared type of a function. This should not need to look at the
279/// function body.
280fn type_for_fn(_db: &dyn HirDatabase, def: Function) -> Ty {
281    TyKind::FnDef(def.into(), Substitution::empty()).intern()
282}
283
284pub(crate) fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> FnSig {
285    match def {
286        CallableDef::Function(f) => fn_sig_for_fn(db, f),
287        CallableDef::Struct(s) => fn_sig_for_struct_constructor(db, s),
288    }
289}
290
291pub(crate) fn fn_sig_for_fn(db: &dyn HirDatabase, def: Function) -> FnSig {
292    let data = def.data(db.upcast());
293    let resolver = def.id.resolver(db.upcast());
294    let params = data
295        .params()
296        .iter()
297        .map(|tr| Ty::from_hir(db, &resolver, data.type_ref_map(), *tr).0)
298        .collect::<Vec<_>>();
299    let ret = Ty::from_hir(db, &resolver, data.type_ref_map(), *data.ret_type()).0;
300    FnSig::from_params_and_return(params, ret)
301}
302
303pub(crate) fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: Struct) -> FnSig {
304    let data = def.data(db.upcast());
305    let resolver = def.id.resolver(db.upcast());
306    let params = data
307        .fields
308        .iter()
309        .map(|(_, field)| Ty::from_hir(db, &resolver, data.type_ref_map(), field.type_ref).0)
310        .collect::<Vec<_>>();
311    let ret = type_for_struct(db, def);
312    FnSig::from_params_and_return(params, ret)
313}
314
315/// Build the type of a struct constructor.
316fn type_for_struct_constructor(db: &dyn HirDatabase, def: Struct) -> Ty {
317    let struct_data = db.struct_data(def.id);
318    if struct_data.kind == StructKind::Tuple {
319        TyKind::FnDef(def.into(), Substitution::empty()).intern()
320    } else {
321        type_for_struct(db, def)
322    }
323}
324
325fn type_for_struct(_db: &dyn HirDatabase, def: Struct) -> Ty {
326    TyKind::Struct(def).intern()
327}
328
329fn type_for_type_alias(db: &dyn HirDatabase, def: TypeAlias) -> Ty {
330    let data = def.data(db.upcast());
331    let resolver = def.id.resolver(db.upcast());
332    let type_ref = def.type_ref(db);
333    Ty::from_hir(db, &resolver, data.type_ref_map(), type_ref).0
334}
335
336pub mod diagnostics {
337    use crate::diagnostics::{CyclicType, PrivateAccess, UnresolvedType};
338    use crate::{
339        diagnostics::DiagnosticSink,
340        type_ref::{LocalTypeRefId, TypeRefSourceMap},
341        FileId, HirDatabase,
342    };
343
344    #[derive(Debug, PartialEq, Eq, Clone)]
345    pub(crate) enum LowerDiagnostic {
346        UnresolvedType { id: LocalTypeRefId },
347        TypeIsPrivate { id: LocalTypeRefId },
348        CyclicType { id: LocalTypeRefId },
349    }
350
351    impl LowerDiagnostic {
352        pub(crate) fn add_to(
353            &self,
354            _db: &dyn HirDatabase,
355            file_id: FileId,
356            source_map: &TypeRefSourceMap,
357            sink: &mut DiagnosticSink,
358        ) {
359            match self {
360                LowerDiagnostic::UnresolvedType { id } => sink.push(UnresolvedType {
361                    file: file_id,
362                    type_ref: source_map.type_ref_syntax(*id).unwrap(),
363                }),
364                LowerDiagnostic::CyclicType { id } => sink.push(CyclicType {
365                    file: file_id,
366                    type_ref: source_map.type_ref_syntax(*id).unwrap(),
367                }),
368                LowerDiagnostic::TypeIsPrivate { id } => sink.push(PrivateAccess {
369                    file: file_id,
370                    expr: source_map.type_ref_syntax(*id).unwrap().syntax_node_ptr(),
371                }),
372            }
373        }
374    }
375}