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