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