Skip to main content

cairo_lang_semantic/
types.rs

1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_defs::db::DefsGroup;
3use cairo_lang_defs::diagnostic_utils::StableLocation;
4use cairo_lang_defs::ids::{
5    EnumId, ExternTypeId, GenericParamId, GenericTypeId, LanguageElementId, ModuleId,
6    NamedLanguageElementId, StructId, TraitTypeId, UnstableSalsaId,
7};
8use cairo_lang_diagnostics::{DiagnosticAdded, Maybe};
9use cairo_lang_filesystem::db::FilesGroup;
10use cairo_lang_proc_macros::{HeapSize, SemanticObject};
11use cairo_lang_syntax::attribute::consts::MUST_USE_ATTR;
12use cairo_lang_syntax::node::ast::PathSegment;
13use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
14use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast};
15use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
16use cairo_lang_utils::{Intern, OptionFrom, define_short_id, extract_matches, try_extract_matches};
17use itertools::{Itertools, chain};
18use num_bigint::BigInt;
19use num_traits::Zero;
20use salsa::Database;
21use sha3::{Digest, Keccak256};
22
23use crate::corelib::{
24    CorelibSemantic, concrete_copy_trait, concrete_destruct_trait, concrete_drop_trait,
25    concrete_panic_destruct_trait, core_box_ty, get_usize_ty, unit_ty,
26};
27use crate::diagnostic::SemanticDiagnosticKind::*;
28use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder};
29use crate::expr::compute::{ComputationContext, compute_expr_semantic};
30use crate::expr::inference::canonic::{CanonicalTrait, ResultNoErrEx};
31use crate::expr::inference::solver::{SemanticSolver, SolutionSet, enrich_lookup_context};
32use crate::expr::inference::{InferenceData, InferenceError, InferenceId, TypeVar};
33use crate::items::attribute::SemanticQueryAttrs;
34use crate::items::constant::{ConstValue, ConstValueId, resolve_const_expr_and_evaluate};
35use crate::items::enm::{EnumSemantic, SemanticEnumEx};
36use crate::items::extern_type::ExternTypeSemantic;
37use crate::items::generics::{GenericParamSemantic, displayable_concrete};
38use crate::items::imp::{ImplId, ImplLookupContext, ImplLookupContextId, ImplSemantic};
39use crate::items::structure::StructSemantic;
40use crate::resolve::{ResolutionContext, ResolvedConcreteItem, ResolvedGenericItem, Resolver};
41use crate::substitution::SemanticRewriter;
42use crate::{ConcreteTraitId, FunctionId, GenericArgumentId, semantic, semantic_object_for_id};
43
44#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
45pub enum TypeLongId<'db> {
46    Concrete(ConcreteTypeId<'db>),
47    /// Some expressions might have invalid types during processing, either due to errors or
48    /// during inference.
49    Tuple(Vec<TypeId<'db>>),
50    Snapshot(TypeId<'db>),
51    GenericParameter(GenericParamId<'db>),
52    Var(TypeVar<'db>),
53    Coupon(FunctionId<'db>),
54    FixedSizeArray {
55        type_id: TypeId<'db>,
56        size: ConstValueId<'db>,
57    },
58    ImplType(ImplTypeId<'db>),
59    Closure(ClosureTypeLongId<'db>),
60    Missing(#[dont_rewrite] DiagnosticAdded),
61}
62impl<'db> OptionFrom<TypeLongId<'db>> for ConcreteTypeId<'db> {
63    fn option_from(other: TypeLongId<'db>) -> Option<Self> {
64        try_extract_matches!(other, TypeLongId::Concrete)
65    }
66}
67
68define_short_id!(TypeId, TypeLongId<'db>);
69semantic_object_for_id!(TypeId, TypeLongId<'a>);
70impl<'db> TypeId<'db> {
71    pub fn missing(db: &'db dyn Database, diag_added: DiagnosticAdded) -> Self {
72        TypeLongId::Missing(diag_added).intern(db)
73    }
74
75    pub fn format(&self, db: &dyn Database) -> String {
76        self.long(db).format(db)
77    }
78
79    /// Returns [Maybe::Err] if the type is [TypeLongId::Missing].
80    pub fn check_not_missing(&self, db: &dyn Database) -> Maybe<()> {
81        if let TypeLongId::Missing(diag_added) = self.long(db) { Err(*diag_added) } else { Ok(()) }
82    }
83
84    /// Returns `true` if the type is [TypeLongId::Missing].
85    pub fn is_missing(&self, db: &dyn Database) -> bool {
86        self.check_not_missing(db).is_err()
87    }
88
89    /// Returns `true` if the type is `()`.
90    pub fn is_unit(&self, db: &dyn Database) -> bool {
91        matches!(self.long(db), TypeLongId::Tuple(types) if types.is_empty())
92    }
93
94    /// Returns the [TypeHead] for a type if available.
95    pub fn head(&self, db: &'db dyn Database) -> Option<TypeHead<'db>> {
96        self.long(db).head(db)
97    }
98
99    /// Returns true if the type does not depend on any generics.
100    pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
101        db.priv_type_is_fully_concrete(*self)
102    }
103
104    /// Returns true if the type does not contain any inference variables.
105    pub fn is_var_free(&self, db: &dyn Database) -> bool {
106        db.priv_type_is_var_free(*self)
107    }
108
109    /// Returns whether the type is phantom.
110    /// Type is considered phantom if it has the `#[phantom]` attribute, or is a tuple or fixed
111    /// sized array containing it.
112    pub fn is_phantom(&self, db: &dyn Database) -> bool {
113        self.long(db).is_phantom(db)
114    }
115
116    /// Short name of the type argument.
117    pub fn short_name(&self, db: &dyn Database) -> String {
118        db.priv_type_short_name(*self)
119    }
120}
121impl<'db> TypeLongId<'db> {
122    pub fn format(&self, db: &dyn Database) -> String {
123        format!("{:?}", self.debug(db))
124    }
125
126    /// Returns the [TypeHead] for a type if available.
127    pub fn head(&self, db: &'db dyn Database) -> Option<TypeHead<'db>> {
128        Some(match self {
129            TypeLongId::Concrete(concrete) => TypeHead::Concrete(concrete.generic_type(db)),
130            TypeLongId::Tuple(_) => TypeHead::Tuple,
131            TypeLongId::Snapshot(inner) => TypeHead::Snapshot(Box::new(inner.head(db)?)),
132            TypeLongId::Coupon(_) => TypeHead::Coupon,
133            TypeLongId::FixedSizeArray { .. } => TypeHead::FixedSizeArray,
134            TypeLongId::GenericParameter(generic_param_id) => TypeHead::Generic(*generic_param_id),
135            TypeLongId::Var(_)
136            | TypeLongId::Missing(_)
137            | TypeLongId::ImplType(_)
138            | TypeLongId::Closure(_) => {
139                return None;
140            }
141        })
142    }
143
144    /// Returns whether the type is phantom.
145    /// Type is considered phantom if it has the `#[phantom]` attribute, (or an other attribute
146    /// declared by a plugin as defining a phantom type), or is a tuple or fixed sized array
147    /// containing it.
148    pub fn is_phantom(&self, db: &dyn Database) -> bool {
149        match self {
150            TypeLongId::Concrete(id) => match id {
151                ConcreteTypeId::Struct(id) => {
152                    let crate_id = id.struct_id(db).long(db).0.owning_crate(db);
153
154                    db.declared_phantom_type_attributes(crate_id)
155                        .iter()
156                        .any(|attr| id.has_attr(db, attr.long(db)).unwrap_or_default())
157                }
158                ConcreteTypeId::Enum(id) => {
159                    let crate_id = id.enum_id(db).long(db).0.owning_crate(db);
160
161                    db.declared_phantom_type_attributes(crate_id)
162                        .iter()
163                        .any(|attr| id.has_attr(db, attr.long(db)).unwrap_or_default())
164                }
165                ConcreteTypeId::Extern(id) => {
166                    let crate_id = id.extern_type_id(db).long(db).0.owning_crate(db);
167
168                    db.declared_phantom_type_attributes(crate_id)
169                        .iter()
170                        .any(|attr| id.has_attr(db, attr.long(db)).unwrap_or_default())
171                }
172            },
173            TypeLongId::Tuple(inner) => inner.iter().any(|ty| ty.is_phantom(db)),
174            TypeLongId::FixedSizeArray { type_id, .. } => type_id.is_phantom(db),
175            TypeLongId::Snapshot(_)
176            | TypeLongId::GenericParameter(_)
177            | TypeLongId::Var(_)
178            | TypeLongId::Coupon(_)
179            | TypeLongId::ImplType(_)
180            | TypeLongId::Missing(_)
181            | TypeLongId::Closure(_) => false,
182        }
183    }
184
185    /// Returns the module id of the given type if applicable.
186    pub fn module_id(&self, db: &'db dyn Database) -> Option<ModuleId<'db>> {
187        match self {
188            TypeLongId::Concrete(concrete) => Some(concrete.generic_type(db).parent_module(db)),
189            TypeLongId::Snapshot(ty) => {
190                let (_n_snapshots, inner_ty) = peel_snapshots(db, *ty);
191                inner_ty.module_id(db)
192            }
193            TypeLongId::GenericParameter(_) => None,
194            TypeLongId::Var(_) => None,
195            TypeLongId::Coupon(function_id) => {
196                function_id.get_concrete(db).generic_function.module_id(db)
197            }
198            TypeLongId::Missing(_) => None,
199            TypeLongId::Tuple(_) => Some(db.core_info().tuple_submodule),
200            TypeLongId::ImplType(_) => None,
201            TypeLongId::FixedSizeArray { .. } => Some(db.core_info().fixed_size_array_submodule),
202            TypeLongId::Closure(closure) => {
203                if let Ok(function_id) = closure.parent_function {
204                    function_id.get_concrete(db).generic_function.module_id(db)
205                } else {
206                    None
207                }
208            }
209        }
210    }
211
212    /// A utility function for extracting the generic parameters arguments from TypeLongId.
213    pub fn extract_generic_params(
214        &self,
215        db: &'db dyn Database,
216        generic_parameters: &mut OrderedHashSet<GenericParamId<'db>>,
217    ) -> Maybe<()> {
218        match self {
219            TypeLongId::Concrete(concrete_type_id) => {
220                for garg in concrete_type_id.generic_args(db) {
221                    garg.extract_generic_params(db, generic_parameters)?;
222                }
223            }
224            TypeLongId::Tuple(tys) => {
225                for ty in tys {
226                    ty.long(db).extract_generic_params(db, generic_parameters)?
227                }
228            }
229            TypeLongId::Snapshot(ty) => {
230                ty.long(db).extract_generic_params(db, generic_parameters)?
231            }
232            TypeLongId::GenericParameter(generic_param) => {
233                generic_parameters.insert(*generic_param);
234            }
235            TypeLongId::Var(_) => {}
236            TypeLongId::Coupon(_) => {}
237            TypeLongId::FixedSizeArray { type_id, .. } => {
238                type_id.long(db).extract_generic_params(db, generic_parameters)?
239            }
240            TypeLongId::ImplType(impl_type_id) => {
241                let concrete_trait_id = impl_type_id.impl_id.concrete_trait(db)?;
242                for garg in concrete_trait_id.generic_args(db) {
243                    garg.extract_generic_params(db, generic_parameters)?;
244                }
245            }
246            TypeLongId::Closure(closure_ty) => {
247                for ty in chain!(
248                    &closure_ty.param_tys,
249                    &closure_ty.captured_types,
250                    std::iter::once(&closure_ty.ret_ty)
251                ) {
252                    ty.long(db).extract_generic_params(db, generic_parameters)?
253                }
254            }
255            TypeLongId::Missing(diag_added) => return Err(*diag_added),
256        };
257        Ok(())
258    }
259}
260impl<'db> DebugWithDb<'db> for TypeLongId<'db> {
261    type Db = dyn Database;
262
263    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
264        match self {
265            TypeLongId::Concrete(concrete) => write!(f, "{}", concrete.format(db)),
266            TypeLongId::Tuple(inner_types) => {
267                if inner_types.len() == 1 {
268                    write!(f, "({},)", inner_types[0].format(db))
269                } else {
270                    write!(f, "({})", inner_types.iter().map(|ty| ty.format(db)).join(", "))
271                }
272            }
273            TypeLongId::Snapshot(ty) => write!(f, "@{}", ty.format(db)),
274            TypeLongId::GenericParameter(generic_param) => {
275                write!(f, "{}", generic_param.name(db).map_or("_", |name| name.long(db)))
276            }
277            TypeLongId::ImplType(impl_type_id) => {
278                write!(
279                    f,
280                    "{:?}::{}",
281                    impl_type_id.impl_id.debug(db),
282                    impl_type_id.ty.name(db).long(db)
283                )
284            }
285            TypeLongId::Var(var) => write!(f, "?{}", var.id.0),
286            TypeLongId::Coupon(function_id) => write!(f, "{}::Coupon", function_id.full_path(db)),
287            TypeLongId::Missing(_) => write!(f, "<missing>"),
288            TypeLongId::FixedSizeArray { type_id, size } => {
289                write!(f, "[{}; {:?}]", type_id.format(db), size.debug(db))
290            }
291            TypeLongId::Closure(closure) => {
292                write!(f, "{:?}", closure.debug(db))
293            }
294        }
295    }
296}
297
298/// Head of a type.
299///
300/// A type that is not one of {generic param, type variable, impl type} has a head, which represents
301/// the kind of the root node in its type tree. This is used for caching queries for fast lookups
302/// when the type is not completely inferred yet.
303#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
304pub enum TypeHead<'db> {
305    Concrete(GenericTypeId<'db>),
306    Snapshot(Box<TypeHead<'db>>),
307    Generic(GenericParamId<'db>),
308    Tuple,
309    Coupon,
310    FixedSizeArray,
311}
312
313#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
314pub enum ConcreteTypeId<'db> {
315    Struct(ConcreteStructId<'db>),
316    Enum(ConcreteEnumId<'db>),
317    Extern(ConcreteExternTypeId<'db>),
318}
319impl<'db> ConcreteTypeId<'db> {
320    pub fn new(
321        db: &'db dyn Database,
322        generic_ty: GenericTypeId<'db>,
323        generic_args: Vec<semantic::GenericArgumentId<'db>>,
324    ) -> Self {
325        match generic_ty {
326            GenericTypeId::Struct(id) => ConcreteTypeId::Struct(
327                ConcreteStructLongId { struct_id: id, generic_args }.intern(db),
328            ),
329            GenericTypeId::Enum(id) => {
330                ConcreteTypeId::Enum(ConcreteEnumLongId { enum_id: id, generic_args }.intern(db))
331            }
332            GenericTypeId::Extern(id) => ConcreteTypeId::Extern(
333                ConcreteExternTypeLongId { extern_type_id: id, generic_args }.intern(db),
334            ),
335        }
336    }
337    pub fn generic_type(&self, db: &'db dyn Database) -> GenericTypeId<'db> {
338        match self {
339            ConcreteTypeId::Struct(id) => GenericTypeId::Struct(id.long(db).struct_id),
340            ConcreteTypeId::Enum(id) => GenericTypeId::Enum(id.long(db).enum_id),
341            ConcreteTypeId::Extern(id) => GenericTypeId::Extern(id.long(db).extern_type_id),
342        }
343    }
344    pub fn generic_args(&self, db: &'db dyn Database) -> Vec<semantic::GenericArgumentId<'db>> {
345        match self {
346            ConcreteTypeId::Struct(id) => id.long(db).generic_args.clone(),
347            ConcreteTypeId::Enum(id) => id.long(db).generic_args.clone(),
348            ConcreteTypeId::Extern(id) => id.long(db).generic_args.clone(),
349        }
350    }
351    pub fn format(&self, db: &dyn Database) -> String {
352        format!("{:?}", self.debug(db))
353    }
354
355    /// Returns whether the type has the `#[must_use]` attribute.
356    pub fn is_must_use(&self, db: &dyn Database) -> Maybe<bool> {
357        match self {
358            ConcreteTypeId::Struct(id) => id.has_attr(db, MUST_USE_ATTR),
359            ConcreteTypeId::Enum(id) => id.has_attr(db, MUST_USE_ATTR),
360            ConcreteTypeId::Extern(id) => id.has_attr(db, MUST_USE_ATTR),
361        }
362    }
363    /// Returns true if the type does not depend on any generics.
364    pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
365        self.generic_args(db)
366            .iter()
367            .all(|generic_argument_id| generic_argument_id.is_fully_concrete(db))
368    }
369    /// Returns true if the type does not contain any inference variables.
370    pub fn is_var_free(&self, db: &dyn Database) -> bool {
371        self.generic_args(db).iter().all(|generic_argument_id| generic_argument_id.is_var_free(db))
372    }
373}
374impl<'db> DebugWithDb<'db> for ConcreteTypeId<'db> {
375    type Db = dyn Database;
376
377    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
378        write!(
379            f,
380            "{}",
381            displayable_concrete(db, &self.generic_type(db).format(db), &self.generic_args(db))
382        )
383    }
384}
385
386#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
387pub struct ConcreteStructLongId<'db> {
388    pub struct_id: StructId<'db>,
389    pub generic_args: Vec<semantic::GenericArgumentId<'db>>,
390}
391define_short_id!(ConcreteStructId, ConcreteStructLongId<'db>);
392semantic_object_for_id!(ConcreteStructId, ConcreteStructLongId<'a>);
393impl<'db> ConcreteStructId<'db> {
394    pub fn struct_id(&self, db: &'db dyn Database) -> StructId<'db> {
395        self.long(db).struct_id
396    }
397}
398impl<'db> DebugWithDb<'db> for ConcreteStructLongId<'db> {
399    type Db = dyn Database;
400
401    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
402        write!(f, "{:?}", ConcreteTypeId::Struct(self.clone().intern(db)).debug(db))
403    }
404}
405
406#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
407pub struct ConcreteEnumLongId<'db> {
408    pub enum_id: EnumId<'db>,
409    pub generic_args: Vec<semantic::GenericArgumentId<'db>>,
410}
411impl<'db> DebugWithDb<'db> for ConcreteEnumLongId<'db> {
412    type Db = dyn Database;
413
414    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
415        write!(f, "{:?}", ConcreteTypeId::Enum(self.clone().intern(db)).debug(db))
416    }
417}
418
419define_short_id!(ConcreteEnumId, ConcreteEnumLongId<'db>);
420semantic_object_for_id!(ConcreteEnumId, ConcreteEnumLongId<'a>);
421impl<'db> ConcreteEnumId<'db> {
422    pub fn enum_id(&self, db: &'db dyn Database) -> EnumId<'db> {
423        self.long(db).enum_id
424    }
425}
426
427#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
428pub struct ConcreteExternTypeLongId<'db> {
429    pub extern_type_id: ExternTypeId<'db>,
430    pub generic_args: Vec<semantic::GenericArgumentId<'db>>,
431}
432define_short_id!(ConcreteExternTypeId, ConcreteExternTypeLongId<'db>);
433semantic_object_for_id!(ConcreteExternTypeId, ConcreteExternTypeLongId<'a>);
434impl<'db> ConcreteExternTypeId<'db> {
435    pub fn extern_type_id(&self, db: &'db dyn Database) -> ExternTypeId<'db> {
436        self.long(db).extern_type_id
437    }
438}
439
440/// A type id of a closure function.
441#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
442pub struct ClosureTypeLongId<'db> {
443    pub param_tys: Vec<TypeId<'db>>,
444    pub ret_ty: TypeId<'db>,
445    /// The set of types captured by the closure, this field is used to determined if the
446    /// closure has Drop, Destruct or PanicDestruct.
447    /// A vector as the fields needs to be hashable.
448    pub captured_types: Vec<TypeId<'db>>,
449    /// The parent function of the closure or an error.
450    pub parent_function: Maybe<FunctionId<'db>>,
451    /// Every closure has a unique type that is based on the stable location of its params.
452    #[dont_rewrite]
453    pub params_location: StableLocation<'db>,
454}
455
456impl<'db> DebugWithDb<'db> for ClosureTypeLongId<'db> {
457    type Db = dyn Database;
458
459    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
460        write!(f, "{{closure@{:?}}}", self.params_location.debug(db))
461    }
462}
463
464/// An impl item of kind type.
465#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject, HeapSize, salsa::Update)]
466pub struct ImplTypeId<'db> {
467    /// The impl the item type is in.
468    impl_id: ImplId<'db>,
469    /// The trait type this impl type "implements".
470    ty: TraitTypeId<'db>,
471}
472impl<'db> ImplTypeId<'db> {
473    /// Creates a new impl type id. For an impl type of a concrete impl, asserts that the trait
474    /// type belongs to the same trait that the impl implements (panics if not).
475    pub fn new(impl_id: ImplId<'db>, ty: TraitTypeId<'db>, db: &'db dyn Database) -> Self {
476        if let crate::items::imp::ImplLongId::Concrete(concrete_impl) = impl_id.long(db) {
477            let impl_def_id = concrete_impl.impl_def_id(db);
478            assert_eq!(Ok(ty.trait_id(db)), db.impl_def_trait(impl_def_id));
479        }
480
481        ImplTypeId { impl_id, ty }
482    }
483    pub fn impl_id(&self) -> ImplId<'db> {
484        self.impl_id
485    }
486    pub fn ty(&self) -> TraitTypeId<'db> {
487        self.ty
488    }
489    pub fn format(&self, db: &dyn Database) -> String {
490        format!("{}::{}", self.impl_id.name(db), self.ty.name(db).long(db))
491    }
492}
493impl<'db> DebugWithDb<'db> for ImplTypeId<'db> {
494    type Db = dyn Database;
495
496    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
497        write!(f, "{}", self.format(db))
498    }
499}
500
501/// A wrapper around ImplTypeById that implements Ord for saving in an ordered collection.
502#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
503pub struct ImplTypeById<'db>(ImplTypeId<'db>);
504
505impl<'db> From<ImplTypeId<'db>> for ImplTypeById<'db> {
506    fn from(impl_type_id: ImplTypeId<'db>) -> Self {
507        Self(impl_type_id)
508    }
509}
510impl<'db> Ord for ImplTypeById<'db> {
511    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
512        self.0
513            .impl_id
514            .get_internal_id()
515            .cmp(&other.0.impl_id.get_internal_id())
516            .then_with(|| self.0.ty.get_internal_id().cmp(&other.0.ty.get_internal_id()))
517    }
518}
519impl<'db> PartialOrd for ImplTypeById<'db> {
520    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
521        Some(self.cmp(other))
522    }
523}
524
525// TODO(spapini): add a query wrapper.
526/// Resolves a type given a module and a path. Used for resolving from non-statement context.
527pub fn resolve_type<'db>(
528    db: &'db dyn Database,
529    diagnostics: &mut SemanticDiagnostics<'db>,
530    resolver: &mut Resolver<'db>,
531    ty_syntax: &ast::Expr<'db>,
532) -> TypeId<'db> {
533    resolve_type_ex(db, diagnostics, resolver, ty_syntax, ResolutionContext::Default)
534}
535/// Resolves a type given a module and a path. Allows defining a resolution context.
536pub fn resolve_type_ex<'db>(
537    db: &'db dyn Database,
538    diagnostics: &mut SemanticDiagnostics<'db>,
539    resolver: &mut Resolver<'db>,
540    ty_syntax: &ast::Expr<'db>,
541    ctx: ResolutionContext<'db, '_>,
542) -> TypeId<'db> {
543    maybe_resolve_type(db, diagnostics, resolver, ty_syntax, ctx)
544        .unwrap_or_else(|diag_added| TypeId::missing(db, diag_added))
545}
546fn maybe_resolve_type<'db>(
547    db: &'db dyn Database,
548    diagnostics: &mut SemanticDiagnostics<'db>,
549    resolver: &mut Resolver<'db>,
550    ty_syntax: &ast::Expr<'db>,
551    mut ctx: ResolutionContext<'db, '_>,
552) -> Maybe<TypeId<'db>> {
553    Ok(match ty_syntax {
554        ast::Expr::Underscore(underscore) => {
555            resolver.inference().new_type_var(Some(underscore.stable_ptr(db).untyped()))
556        }
557        ast::Expr::Path(path) => {
558            match resolver.resolve_concrete_path_ex(
559                diagnostics,
560                path,
561                NotFoundItemType::Type,
562                ctx,
563            )? {
564                ResolvedConcreteItem::Type(ty) => ty,
565                _ => {
566                    return Err(diagnostics.report(path.stable_ptr(db), NotAType));
567                }
568            }
569        }
570        ast::Expr::Parenthesized(expr_syntax) => {
571            resolve_type_ex(db, diagnostics, resolver, &expr_syntax.expr(db), ctx)
572        }
573        ast::Expr::Tuple(tuple_syntax) => {
574            let sub_tys = tuple_syntax
575                .expressions(db)
576                .elements(db)
577                .map(|subexpr_syntax| {
578                    resolve_type_ex(
579                        db,
580                        diagnostics,
581                        resolver,
582                        &subexpr_syntax,
583                        match ctx {
584                            ResolutionContext::Default => ResolutionContext::Default,
585                            ResolutionContext::ModuleItem(id) => ResolutionContext::ModuleItem(id),
586                            ResolutionContext::Statement(ref mut env) => {
587                                ResolutionContext::Statement(env)
588                            }
589                        },
590                    )
591                })
592                .collect();
593            TypeLongId::Tuple(sub_tys).intern(db)
594        }
595        ast::Expr::Unary(unary_syntax)
596            if matches!(unary_syntax.op(db), ast::UnaryOperator::At(_)) =>
597        {
598            let ty = resolve_type_ex(db, diagnostics, resolver, &unary_syntax.expr(db), ctx);
599            TypeLongId::Snapshot(ty).intern(db)
600        }
601        // TODO(TomerStarkware): make sure this is unreachable.
602        ast::Expr::Unary(unary_syntax)
603            if matches!(unary_syntax.op(db), ast::UnaryOperator::Desnap(_)) =>
604        {
605            let ty = resolve_type_ex(db, diagnostics, resolver, &unary_syntax.expr(db), ctx);
606            if let Some(desnapped_ty) = try_extract_matches!(ty.long(db), TypeLongId::Snapshot) {
607                *desnapped_ty
608            } else {
609                return Err(diagnostics.report(ty_syntax.stable_ptr(db), DerefNonRef { ty }));
610            }
611        }
612        ast::Expr::Unary(unary_syntax)
613            if matches!(unary_syntax.op(db), ast::UnaryOperator::Reference(_)) =>
614        {
615            if !are_repr_ptrs_enabled(db, resolver.module_id) {
616                return Err(diagnostics.report(ty_syntax.stable_ptr(db), ReprPtrsDisabled));
617            }
618            let inner_ty = resolve_type_ex(db, diagnostics, resolver, &unary_syntax.expr(db), ctx);
619            let snapshot_ty = TypeLongId::Snapshot(inner_ty).intern(db);
620            core_box_ty(db, snapshot_ty)
621        }
622        ast::Expr::FixedSizeArray(array_syntax) => {
623            let Ok(ty) = &array_syntax.exprs(db).elements(db).exactly_one() else {
624                return Err(
625                    diagnostics.report(ty_syntax.stable_ptr(db), FixedSizeArrayTypeNonSingleType)
626                );
627            };
628            let ty = resolve_type_ex(db, diagnostics, resolver, ty, ctx);
629            let size =
630                match extract_fixed_size_array_size(db, diagnostics, array_syntax, resolver)? {
631                    Some(size) => size,
632                    None => {
633                        return Err(diagnostics
634                            .report(ty_syntax.stable_ptr(db), FixedSizeArrayTypeEmptySize));
635                    }
636                };
637            TypeLongId::FixedSizeArray { type_id: ty, size }.intern(db)
638        }
639        _ => {
640            return Err(diagnostics.report(ty_syntax.stable_ptr(db), UnknownType));
641        }
642    })
643}
644
645/// A generic argument which is not fully inferred. Used to avoid cycles in the inference.
646#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
647pub enum ShallowGenericArg<'db> {
648    /// The generic argument is a generic parameter.
649    GenericParameter(GenericParamId<'db>),
650    /// The generic argument is a generic type.
651    GenericType(GenericTypeId<'db>),
652    Snapshot(Box<ShallowGenericArg<'db>>),
653    Tuple,
654    FixedSizeArray,
655}
656
657impl<'db> ShallowGenericArg<'db> {
658    /// Returns the module id of the shallow generic argument.
659    pub fn module_id(&self, db: &'db dyn Database) -> Option<ModuleId<'db>> {
660        match self {
661            ShallowGenericArg::GenericParameter(_) => None,
662            ShallowGenericArg::GenericType(ty) => Some(ty.parent_module(db)),
663            ShallowGenericArg::Snapshot(inner) => inner.module_id(db),
664            ShallowGenericArg::Tuple => TypeLongId::Tuple(vec![]).module_id(db),
665            ShallowGenericArg::FixedSizeArray => TypeLongId::FixedSizeArray {
666                type_id: unit_ty(db),
667                size: ConstValue::Struct(vec![], unit_ty(db)).intern(db),
668            }
669            .module_id(db),
670        }
671    }
672    pub fn head(&self) -> TypeHead<'db> {
673        match self {
674            ShallowGenericArg::GenericParameter(param) => TypeHead::Generic(*param),
675            ShallowGenericArg::GenericType(ty) => TypeHead::Concrete(*ty),
676            ShallowGenericArg::Snapshot(inner) => TypeHead::Snapshot(Box::new(inner.head())),
677            ShallowGenericArg::Tuple => TypeHead::Tuple,
678            ShallowGenericArg::FixedSizeArray => TypeHead::FixedSizeArray,
679        }
680    }
681}
682/// Resolves a shallow generic argument from a syntax node.
683pub fn maybe_resolve_shallow_generic_arg_type<'db>(
684    db: &'db dyn Database,
685    diagnostics: &mut SemanticDiagnostics<'db>,
686    resolver: &mut Resolver<'db>,
687    ty_syntax: &ast::Expr<'db>,
688) -> Option<ShallowGenericArg<'db>> {
689    Some(match ty_syntax {
690        ast::Expr::Path(path) => {
691            if let [PathSegment::Simple(path)] =
692                path.segments(db).elements(db).collect_vec().as_slice()
693                && let Some(ResolvedConcreteItem::Type(ty)) =
694                    resolver.determine_base_item_in_local_scope(&path.ident(db))
695            {
696                let param = extract_matches!(ty.long(db), TypeLongId::GenericParameter);
697                return Some(ShallowGenericArg::GenericParameter(*param));
698            }
699
700            match resolver
701                .resolve_generic_path_with_args(
702                    diagnostics,
703                    path,
704                    NotFoundItemType::Type,
705                    ResolutionContext::Default,
706                )
707                .ok()?
708            {
709                ResolvedGenericItem::GenericType(ty) => ShallowGenericArg::GenericType(ty),
710                _ => {
711                    return None;
712                }
713            }
714        }
715        ast::Expr::Parenthesized(expr_syntax) => maybe_resolve_shallow_generic_arg_type(
716            db,
717            diagnostics,
718            resolver,
719            &expr_syntax.expr(db),
720        )?,
721        ast::Expr::Tuple(_) => ShallowGenericArg::Tuple,
722        ast::Expr::Unary(unary_syntax)
723            if matches!(unary_syntax.op(db), ast::UnaryOperator::At(_)) =>
724        {
725            ShallowGenericArg::Snapshot(Box::new(maybe_resolve_shallow_generic_arg_type(
726                db,
727                diagnostics,
728                resolver,
729                &unary_syntax.expr(db),
730            )?))
731        }
732        ast::Expr::Unary(unary_syntax)
733            if matches!(unary_syntax.op(db), ast::UnaryOperator::Desnap(_)) =>
734        {
735            maybe_resolve_shallow_generic_arg_type(
736                db,
737                diagnostics,
738                resolver,
739                &unary_syntax.expr(db),
740            )?
741        }
742        ast::Expr::FixedSizeArray(_) => ShallowGenericArg::FixedSizeArray,
743        _ => {
744            return None;
745        }
746    })
747}
748
749/// Extracts the size of a fixed size array, or none if the size is missing. Reports an error if the
750/// size is not a numeric literal.
751pub fn extract_fixed_size_array_size<'db>(
752    db: &'db dyn Database,
753    diagnostics: &mut SemanticDiagnostics<'db>,
754    syntax: &ast::ExprFixedSizeArray<'db>,
755    resolver: &mut Resolver<'db>,
756) -> Maybe<Option<ConstValueId<'db>>> {
757    match syntax.size(db) {
758        ast::OptionFixedSizeArraySize::FixedSizeArraySize(size_clause) => {
759            let mut ctx = ComputationContext::new_global(db, diagnostics, resolver);
760            let size_expr_syntax = size_clause.size(db);
761            let size = compute_expr_semantic(&mut ctx, &size_expr_syntax);
762            let const_value = resolve_const_expr_and_evaluate(
763                db,
764                &mut ctx,
765                &size,
766                size_expr_syntax.stable_ptr(db).untyped(),
767                get_usize_ty(db),
768                false,
769            );
770            if matches!(
771                const_value.long(db),
772                ConstValue::Int(_, _) | ConstValue::Generic(_) | ConstValue::ImplConstant(_)
773            ) {
774                Ok(Some(const_value))
775            } else {
776                Err(diagnostics.report(syntax.stable_ptr(db), FixedSizeArrayNonNumericSize))
777            }
778        }
779        ast::OptionFixedSizeArraySize::Empty(_) => Ok(None),
780    }
781}
782
783/// Verifies that a given fixed size array size is within limits, and adds a diagnostic if not.
784pub fn verify_fixed_size_array_size<'db>(
785    db: &'db dyn Database,
786    diagnostics: &mut SemanticDiagnostics<'db>,
787    size: &BigInt,
788    syntax: &ast::ExprFixedSizeArray<'db>,
789) -> Maybe<()> {
790    if size > &BigInt::from(i16::MAX) {
791        return Err(diagnostics.report(syntax.stable_ptr(db), FixedSizeArraySizeTooBig));
792    }
793    Ok(())
794}
795
796#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
797pub struct TypeInfo<'db> {
798    pub droppable: Result<ImplId<'db>, InferenceError<'db>>,
799    pub copyable: Result<ImplId<'db>, InferenceError<'db>>,
800    pub destruct_impl: Result<ImplId<'db>, InferenceError<'db>>,
801    pub panic_destruct_impl: Result<ImplId<'db>, InferenceError<'db>>,
802}
803
804/// Checks if there is at least one impl that can be inferred for a specific concrete trait.
805pub fn get_impl_at_context<'db>(
806    db: &'db dyn Database,
807    lookup_context: ImplLookupContextId<'db>,
808    concrete_trait_id: ConcreteTraitId<'db>,
809    stable_ptr: Option<SyntaxStablePtrId<'db>>,
810) -> Result<ImplId<'db>, InferenceError<'db>> {
811    let constrains =
812        db.generic_params_type_constraints(lookup_context.long(db).generic_params.clone());
813    if constrains.is_empty() && concrete_trait_id.is_var_free(db) {
814        return solve_concrete_trait_no_constraints(db, lookup_context, concrete_trait_id);
815    }
816    let mut inference_data = InferenceData::new(InferenceId::NoContext);
817    let mut inference = inference_data.inference(db);
818    inference.conform_generic_params_type_constraints(constrains);
819    // It's ok to consume the errors without reporting as this is a helper function meant to find an
820    // impl and return it, but it's ok if the impl can't be found.
821    let impl_id = inference.new_impl_var(concrete_trait_id, stable_ptr, lookup_context);
822    if let Err(err_set) = inference.finalize_without_reporting() {
823        return Err(inference
824            .consume_error_without_reporting(err_set)
825            .expect("Error couldn't be already consumed"));
826    };
827    Ok(inference.rewrite(impl_id).no_err())
828}
829
830/// Implementation of [TypesSemantic::single_value_type].
831fn single_value_type(db: &dyn Database, ty: TypeId<'_>) -> Maybe<bool> {
832    Ok(match ty.long(db) {
833        TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
834            ConcreteTypeId::Struct(id) => {
835                for member in db.struct_members(id.struct_id(db))?.values() {
836                    if !db.single_value_type(member.ty)? {
837                        return Ok(false);
838                    }
839                }
840                true
841            }
842            ConcreteTypeId::Enum(id) => {
843                let variants = db.enum_variants(id.enum_id(db))?;
844                if variants.len() != 1 {
845                    return Ok(false);
846                }
847
848                db.single_value_type(
849                    db.variant_semantic(id.enum_id(db), *variants.values().next().unwrap())?.ty,
850                )?
851            }
852            ConcreteTypeId::Extern(_) => false,
853        },
854        TypeLongId::Tuple(types) => {
855            for ty in types {
856                if !db.single_value_type(*ty)? {
857                    return Ok(false);
858                }
859            }
860            true
861        }
862        TypeLongId::Snapshot(ty) => db.single_value_type(*ty)?,
863        TypeLongId::GenericParameter(_)
864        | TypeLongId::Var(_)
865        | TypeLongId::Missing(_)
866        | TypeLongId::Coupon(_)
867        | TypeLongId::ImplType(_)
868        | TypeLongId::Closure(_) => false,
869        TypeLongId::FixedSizeArray { type_id, size } => {
870            db.single_value_type(*type_id)?
871                || matches!(size.long(db),
872                            ConstValue::Int(value, _) if value.is_zero())
873        }
874    })
875}
876
877/// Query implementation of [TypesSemantic::single_value_type].
878#[salsa::tracked]
879fn single_value_type_tracked<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> Maybe<bool> {
880    single_value_type(db, ty)
881}
882
883/// Adds diagnostics for a type, post semantic analysis of types.
884pub fn add_type_based_diagnostics<'db>(
885    db: &'db dyn Database,
886    diagnostics: &mut SemanticDiagnostics<'db>,
887    ty: TypeId<'db>,
888    stable_ptr: impl Into<SyntaxStablePtrId<'db>> + Copy,
889) {
890    if db.type_size_info(ty) == Ok(TypeSizeInformation::Infinite) {
891        diagnostics.report(stable_ptr, InfiniteSizeType(ty));
892    }
893    if let TypeLongId::Concrete(ConcreteTypeId::Extern(extrn)) = ty.long(db) {
894        let long_id = extrn.long(db);
895        if long_id.extern_type_id.name(db).long(db) == "Array"
896            && let [GenericArgumentId::Type(arg_ty)] = &long_id.generic_args[..]
897            && db.type_size_info(*arg_ty) == Ok(TypeSizeInformation::ZeroSized)
898        {
899            diagnostics.report(stable_ptr, ArrayOfZeroSizedElements(*arg_ty));
900        }
901    }
902}
903
904#[derive(Clone, Debug, PartialEq, Eq)]
905pub enum TypeSizeInformation {
906    /// The type has an infinite size - caused by a recursion in it.
907    /// If the type simply holds an infinite type, it would be considered `Other`, for diagnostics
908    /// reasons.
909    Infinite,
910    /// The type is zero size.
911    ZeroSized,
912    /// The typed has some none zero size.
913    Other,
914}
915
916/// Implementation of [TypesSemantic::type_size_info].
917fn type_size_info(db: &dyn Database, ty: TypeId<'_>) -> Maybe<TypeSizeInformation> {
918    match ty.long(db) {
919        TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
920            ConcreteTypeId::Struct(id) => {
921                if check_all_type_are_zero_sized(
922                    db,
923                    db.concrete_struct_members(*id)?.iter().map(|(_, member)| &member.ty),
924                )? {
925                    return Ok(TypeSizeInformation::ZeroSized);
926                }
927            }
928            ConcreteTypeId::Enum(id) => {
929                for variant in &db.concrete_enum_variants(*id)? {
930                    // Recursive calling in order to find infinite sized types.
931                    db.type_size_info(variant.ty)?;
932                }
933            }
934            ConcreteTypeId::Extern(_) => {}
935        },
936        TypeLongId::Tuple(types) => {
937            if check_all_type_are_zero_sized(db, types.iter())? {
938                return Ok(TypeSizeInformation::ZeroSized);
939            }
940        }
941        TypeLongId::Snapshot(ty) => {
942            if db.type_size_info(*ty)? == TypeSizeInformation::ZeroSized {
943                return Ok(TypeSizeInformation::ZeroSized);
944            }
945        }
946        TypeLongId::Closure(closure_ty) => {
947            if check_all_type_are_zero_sized(db, closure_ty.captured_types.iter())? {
948                return Ok(TypeSizeInformation::ZeroSized);
949            }
950        }
951        TypeLongId::Coupon(_) => return Ok(TypeSizeInformation::ZeroSized),
952        TypeLongId::GenericParameter(_)
953        | TypeLongId::Var(_)
954        | TypeLongId::Missing(_)
955        | TypeLongId::ImplType(_) => {}
956        TypeLongId::FixedSizeArray { type_id, size } => {
957            if matches!(size.long(db), ConstValue::Int(value,_) if value.is_zero())
958                || db.type_size_info(*type_id)? == TypeSizeInformation::ZeroSized
959            {
960                return Ok(TypeSizeInformation::ZeroSized);
961            }
962        }
963    }
964    Ok(TypeSizeInformation::Other)
965}
966
967/// Query implementation of [TypesSemantic::type_size_info].
968#[salsa::tracked(cycle_result=type_size_info_cycle)]
969fn type_size_info_tracked<'db>(
970    db: &'db dyn Database,
971    ty: TypeId<'db>,
972) -> Maybe<TypeSizeInformation> {
973    type_size_info(db, ty)
974}
975
976/// Checks if all types in the iterator are zero sized.
977fn check_all_type_are_zero_sized<'a>(
978    db: &dyn Database,
979    types: impl Iterator<Item = &'a TypeId<'a>>,
980) -> Maybe<bool> {
981    let mut zero_sized = true;
982    for ty in types {
983        if db.type_size_info(*ty)? != TypeSizeInformation::ZeroSized {
984            zero_sized = false;
985        }
986    }
987    Ok(zero_sized)
988}
989
990/// Cycle handling of [TypesSemantic::type_size_info].
991fn type_size_info_cycle<'db>(
992    _db: &'db dyn Database,
993    _id: salsa::Id,
994    _ty: TypeId<'db>,
995) -> Maybe<TypeSizeInformation> {
996    Ok(TypeSizeInformation::Infinite)
997}
998
999// TODO(spapini): type info lookup for non generic types needs to not depend on lookup_context.
1000// This is to ensure that sierra generator will see a consistent type info of types.
1001/// Implementation of [TypesSemantic::type_info].
1002fn type_info<'db>(
1003    db: &'db dyn Database,
1004    lookup_context: ImplLookupContextId<'db>,
1005    ty: TypeId<'db>,
1006) -> TypeInfo<'db> {
1007    // Dummy stable pointer for type inference variables, since inference is disabled.
1008    let droppable = get_impl_at_context(db, lookup_context, concrete_drop_trait(db, ty), None);
1009    let copyable = get_impl_at_context(db, lookup_context, concrete_copy_trait(db, ty), None);
1010    let destruct_impl =
1011        get_impl_at_context(db, lookup_context, concrete_destruct_trait(db, ty), None);
1012    let panic_destruct_impl =
1013        get_impl_at_context(db, lookup_context, concrete_panic_destruct_trait(db, ty), None);
1014    TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl }
1015}
1016
1017/// Query implementation of [TypesSemantic::type_info].
1018#[salsa::tracked]
1019fn type_info_tracked<'db>(
1020    db: &'db dyn Database,
1021    lookup_context: ImplLookupContextId<'db>,
1022    ty: TypeId<'db>,
1023) -> TypeInfo<'db> {
1024    type_info(db, lookup_context, ty)
1025}
1026
1027/// Solves a concrete trait without any constraints.
1028/// Only works when the given trait is var free.
1029fn solve_concrete_trait_no_constraints<'db>(
1030    db: &'db dyn Database,
1031    lookup_context: ImplLookupContextId<'db>,
1032    id: ConcreteTraitId<'db>,
1033) -> Result<ImplId<'db>, InferenceError<'db>> {
1034    let mut lookup_context = lookup_context.long(db).clone();
1035    enrich_lookup_context(db, id, &mut lookup_context);
1036    let lookup_context = lookup_context.intern(db);
1037    match db.canonic_trait_solutions(
1038        CanonicalTrait { id, mappings: Default::default() },
1039        lookup_context,
1040        Default::default(),
1041    )? {
1042        SolutionSet::None => Err(InferenceError::NoImplsFound(id)),
1043        SolutionSet::Unique(solution) => Ok(solution.0),
1044        SolutionSet::Ambiguous(ambiguity) => Err(InferenceError::Ambiguity(ambiguity)),
1045    }
1046}
1047
1048/// Implementation of [TypesSemantic::copyable].
1049fn copyable<'db>(
1050    db: &'db dyn Database,
1051    ty: TypeId<'db>,
1052) -> Result<ImplId<'db>, InferenceError<'db>> {
1053    solve_concrete_trait_no_constraints(
1054        db,
1055        ImplLookupContext::new_from_type(ty, db).intern(db),
1056        concrete_copy_trait(db, ty),
1057    )
1058}
1059
1060/// Query implementation of [TypesSemantic::copyable].
1061#[salsa::tracked]
1062fn copyable_tracked<'db>(
1063    db: &'db dyn Database,
1064    ty: TypeId<'db>,
1065) -> Result<ImplId<'db>, InferenceError<'db>> {
1066    copyable(db, ty)
1067}
1068
1069/// Implementation of [TypesSemantic::droppable].
1070fn droppable<'db>(
1071    db: &'db dyn Database,
1072    ty: TypeId<'db>,
1073) -> Result<ImplId<'db>, InferenceError<'db>> {
1074    solve_concrete_trait_no_constraints(
1075        db,
1076        ImplLookupContext::new_from_type(ty, db).intern(db),
1077        concrete_drop_trait(db, ty),
1078    )
1079}
1080
1081/// Query implementation of [TypesSemantic::droppable].
1082#[salsa::tracked]
1083fn droppable_tracked<'db>(
1084    db: &'db dyn Database,
1085    ty: TypeId<'db>,
1086) -> Result<ImplId<'db>, InferenceError<'db>> {
1087    droppable(db, ty)
1088}
1089
1090/// Implementation of [PrivTypesSemantic::priv_type_is_fully_concrete].
1091fn priv_type_is_fully_concrete(db: &dyn Database, ty: TypeId<'_>) -> bool {
1092    match ty.long(db) {
1093        TypeLongId::Concrete(concrete_type_id) => concrete_type_id.is_fully_concrete(db),
1094        TypeLongId::Tuple(types) => types.iter().all(|ty| ty.is_fully_concrete(db)),
1095        TypeLongId::Snapshot(ty) => ty.is_fully_concrete(db),
1096        TypeLongId::GenericParameter(_)
1097        | TypeLongId::Var(_)
1098        | TypeLongId::Missing(_)
1099        | TypeLongId::ImplType(_) => false,
1100        TypeLongId::Coupon(function_id) => function_id.is_fully_concrete(db),
1101        TypeLongId::FixedSizeArray { type_id, size } => {
1102            type_id.is_fully_concrete(db) && size.is_fully_concrete(db)
1103        }
1104        TypeLongId::Closure(closure) => {
1105            closure.parent_function.map(|id| id.is_fully_concrete(db)).unwrap_or(true)
1106                && closure.param_tys.iter().all(|param| param.is_fully_concrete(db))
1107                && closure.ret_ty.is_fully_concrete(db)
1108        }
1109    }
1110}
1111
1112/// Query implementation of [PrivTypesSemantic::priv_type_is_fully_concrete].
1113#[salsa::tracked]
1114pub fn priv_type_is_fully_concrete_tracked<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> bool {
1115    priv_type_is_fully_concrete(db, ty)
1116}
1117
1118pub fn priv_type_is_var_free<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> bool {
1119    match ty.long(db) {
1120        TypeLongId::Concrete(concrete_type_id) => concrete_type_id.is_var_free(db),
1121        TypeLongId::Tuple(types) => types.iter().all(|ty| ty.is_var_free(db)),
1122        TypeLongId::Snapshot(ty) => ty.is_var_free(db),
1123        TypeLongId::Var(_) => false,
1124        TypeLongId::GenericParameter(_) | TypeLongId::Missing(_) => true,
1125        TypeLongId::Coupon(function_id) => function_id.is_var_free(db),
1126        TypeLongId::FixedSizeArray { type_id, size } => {
1127            type_id.is_var_free(db) && size.is_var_free(db)
1128        }
1129        // TODO(TomerStarkware): consider rename the function to `priv_type_might_need_rewrite`.
1130        // a var free ImplType needs to be rewritten if has impl bounds constraints.
1131        TypeLongId::ImplType(_) => false,
1132        TypeLongId::Closure(closure) => {
1133            chain!(&closure.captured_types, &closure.param_tys).all(|param| param.is_var_free(db))
1134                && closure.ret_ty.is_var_free(db)
1135        }
1136    }
1137}
1138
1139/// Query implementation of [PrivTypesSemantic::priv_type_is_var_free].
1140#[salsa::tracked]
1141pub fn priv_type_is_var_free_tracked<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> bool {
1142    priv_type_is_var_free(db, ty)
1143}
1144
1145pub fn priv_type_short_name(db: &dyn Database, ty: TypeId<'_>) -> String {
1146    match ty.long(db) {
1147        TypeLongId::Concrete(concrete_type_id) => {
1148            let mut result = concrete_type_id.generic_type(db).format(db);
1149            let mut generic_args = concrete_type_id.generic_args(db).into_iter().peekable();
1150            if generic_args.peek().is_some() {
1151                result.push_str("::<h0x");
1152                let mut hasher = Keccak256::new();
1153                for arg in generic_args {
1154                    hasher.update(arg.short_name(db).as_bytes());
1155                }
1156                for c in hasher.finalize() {
1157                    result.push_str(&format!("{c:x}"));
1158                }
1159                result.push('>');
1160            }
1161            result
1162        }
1163        TypeLongId::Tuple(types) => {
1164            let mut result = String::from("(h0x");
1165            let mut hasher = Keccak256::new();
1166            for ty in types {
1167                hasher.update(ty.short_name(db).as_bytes());
1168            }
1169            for c in hasher.finalize() {
1170                result.push_str(&format!("{c:x}"));
1171            }
1172            result.push(')');
1173            result
1174        }
1175        TypeLongId::Snapshot(ty) => {
1176            format!("@{}", ty.short_name(db))
1177        }
1178        TypeLongId::FixedSizeArray { type_id, size } => {
1179            format!("[{}; {:?}", type_id.short_name(db), size.debug(db))
1180        }
1181        other => other.format(db),
1182    }
1183}
1184
1185#[salsa::tracked]
1186pub fn priv_type_short_name_tracked<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> String {
1187    priv_type_short_name(db, ty)
1188}
1189
1190/// Peels all wrapping Snapshot (`@`) from the type.
1191/// Returns the number of peeled snapshots and the inner type.
1192pub fn peel_snapshots<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> (usize, TypeLongId<'db>) {
1193    peel_snapshots_ex(db, ty.long(db).clone())
1194}
1195
1196/// Same as `peel_snapshots`, but takes a `TypeLongId` instead of a `TypeId`.
1197pub fn peel_snapshots_ex<'db>(
1198    db: &'db dyn Database,
1199    mut long_ty: TypeLongId<'db>,
1200) -> (usize, TypeLongId<'db>) {
1201    let mut n_snapshots = 0;
1202    while let TypeLongId::Snapshot(ty) = long_ty {
1203        long_ty = ty.long(db).clone();
1204        n_snapshots += 1;
1205    }
1206    (n_snapshots, long_ty)
1207}
1208
1209/// Wraps a type with Snapshot (`@`) `n_snapshots` times.
1210pub fn wrap_in_snapshots<'db>(
1211    db: &'db dyn Database,
1212    mut ty: TypeId<'db>,
1213    n_snapshots: usize,
1214) -> TypeId<'db> {
1215    for _ in 0..n_snapshots {
1216        ty = TypeLongId::Snapshot(ty).intern(db);
1217    }
1218    ty
1219}
1220
1221/// Returns `true` if coupons are enabled in the module.
1222pub(crate) fn are_coupons_enabled(db: &dyn Database, module_id: ModuleId<'_>) -> bool {
1223    let owning_crate = module_id.owning_crate(db);
1224    let Some(config) = db.crate_config(owning_crate) else { return false };
1225    config.settings.experimental_features.coupons
1226}
1227
1228/// Returns `true` if representation pointers are enabled in the module.
1229pub(crate) fn are_repr_ptrs_enabled(db: &dyn Database, module_id: ModuleId<'_>) -> bool {
1230    let owning_crate = module_id.owning_crate(db);
1231    db.crate_config(owning_crate)
1232        .is_some_and(|config| config.settings.experimental_features.repr_ptrs)
1233}
1234
1235/// Trait for types-related semantic queries.
1236pub trait TypesSemantic<'db>: Database {
1237    /// Returns the generic params of a generic type.
1238    fn generic_type_generic_params(
1239        &'db self,
1240        generic_type: GenericTypeId<'db>,
1241    ) -> Maybe<&'db [semantic::GenericParam<'db>]> {
1242        match generic_type {
1243            GenericTypeId::Struct(id) => self.struct_generic_params(id),
1244            GenericTypeId::Enum(id) => self.enum_generic_params(id),
1245            GenericTypeId::Extern(id) => self.extern_type_declaration_generic_params(id),
1246        }
1247    }
1248    /// Returns true if there is only one value for the given type and hence the values of the given
1249    /// type are all interchangeable.
1250    /// Examples include the unit type tuple of a unit type and empty structs.
1251    /// Always returns false for extern types.
1252    fn single_value_type(&'db self, ty: TypeId<'db>) -> Maybe<bool> {
1253        single_value_type_tracked(self.as_dyn_database(), ty)
1254    }
1255    /// Returns the type size information for the given type.
1256    fn type_size_info(&'db self, ty: TypeId<'db>) -> Maybe<TypeSizeInformation> {
1257        type_size_info_tracked(self.as_dyn_database(), ty)
1258    }
1259    /// Returns the type info for a type in a context.
1260    fn type_info(
1261        &'db self,
1262        lookup_context: ImplLookupContextId<'db>,
1263        ty: TypeId<'db>,
1264    ) -> TypeInfo<'db> {
1265        type_info_tracked(self.as_dyn_database(), lookup_context, ty)
1266    }
1267    /// Returns the `Copy` impl for a type in general context.
1268    fn copyable(&'db self, ty: TypeId<'db>) -> Result<ImplId<'db>, InferenceError<'db>> {
1269        copyable_tracked(self.as_dyn_database(), ty)
1270    }
1271    /// Returns the `Drop` impl for a type in general context.
1272    fn droppable(&'db self, ty: TypeId<'db>) -> Result<ImplId<'db>, InferenceError<'db>> {
1273        droppable_tracked(self.as_dyn_database(), ty)
1274    }
1275}
1276impl<'db, T: Database + ?Sized> TypesSemantic<'db> for T {}
1277
1278/// Private trait for types-related semantic queries.
1279pub trait PrivTypesSemantic<'db>: Database {
1280    /// Private query to check if a type is fully concrete.
1281    fn priv_type_is_fully_concrete(&self, ty: TypeId<'db>) -> bool {
1282        priv_type_is_fully_concrete_tracked(self.as_dyn_database(), ty)
1283    }
1284    /// Private query to check if a type contains no variables.
1285    fn priv_type_is_var_free(&self, ty: TypeId<'db>) -> bool {
1286        priv_type_is_var_free_tracked(self.as_dyn_database(), ty)
1287    }
1288    /// Private query for a shorter unique name for types.
1289    fn priv_type_short_name(&self, ty: TypeId<'db>) -> String {
1290        priv_type_short_name_tracked(self.as_dyn_database(), ty)
1291    }
1292}
1293impl<'db, T: Database + ?Sized> PrivTypesSemantic<'db> for T {}