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