cairo_lang_semantic/
types.rs

1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_defs::diagnostic_utils::StableLocation;
3use cairo_lang_defs::ids::{
4    EnumId, ExternTypeId, GenericParamId, GenericTypeId, ModuleFileId, NamedLanguageElementId,
5    StructId, TraitTypeId, UnstableSalsaId,
6};
7use cairo_lang_diagnostics::{DiagnosticAdded, Maybe};
8use cairo_lang_proc_macros::SemanticObject;
9use cairo_lang_syntax::attribute::consts::MUST_USE_ATTR;
10use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
11use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast};
12use cairo_lang_utils::{Intern, LookupIntern, OptionFrom, define_short_id, try_extract_matches};
13use itertools::Itertools;
14use num_bigint::BigInt;
15use num_traits::Zero;
16use sha3::{Digest, Keccak256};
17use smol_str::SmolStr;
18
19use crate::corelib::{
20    concrete_copy_trait, concrete_destruct_trait, concrete_drop_trait,
21    concrete_panic_destruct_trait, get_usize_ty,
22};
23use crate::db::SemanticGroup;
24use crate::diagnostic::SemanticDiagnosticKind::*;
25use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder};
26use crate::expr::compute::{
27    ComputationContext, ContextFunction, Environment, compute_expr_semantic,
28};
29use crate::expr::inference::canonic::ResultNoErrEx;
30use crate::expr::inference::{InferenceData, InferenceError, InferenceId, TypeVar};
31use crate::items::attribute::SemanticQueryAttrs;
32use crate::items::constant::{ConstValue, ConstValueId, resolve_const_expr_and_evaluate};
33use crate::items::imp::{ImplId, ImplLookupContext};
34use crate::resolve::{ResolvedConcreteItem, Resolver};
35use crate::substitution::SemanticRewriter;
36use crate::{ConcreteTraitId, FunctionId, GenericArgumentId, semantic, semantic_object_for_id};
37
38#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
39pub enum TypeLongId {
40    Concrete(ConcreteTypeId),
41    /// Some expressions might have invalid types during processing, either due to errors or
42    /// during inference.
43    Tuple(Vec<TypeId>),
44    Snapshot(TypeId),
45    GenericParameter(GenericParamId),
46    Var(TypeVar),
47    Coupon(FunctionId),
48    FixedSizeArray {
49        type_id: TypeId,
50        size: ConstValueId,
51    },
52    ImplType(ImplTypeId),
53    TraitType(TraitTypeId),
54    Closure(ClosureTypeLongId),
55    Missing(#[dont_rewrite] DiagnosticAdded),
56}
57impl OptionFrom<TypeLongId> for ConcreteTypeId {
58    fn option_from(other: TypeLongId) -> Option<Self> {
59        try_extract_matches!(other, TypeLongId::Concrete)
60    }
61}
62
63define_short_id!(TypeId, TypeLongId, SemanticGroup, lookup_intern_type, intern_type);
64semantic_object_for_id!(TypeId, lookup_intern_type, intern_type, TypeLongId);
65impl TypeId {
66    pub fn missing(db: &dyn SemanticGroup, diag_added: DiagnosticAdded) -> Self {
67        TypeLongId::Missing(diag_added).intern(db)
68    }
69
70    pub fn format(&self, db: &dyn SemanticGroup) -> String {
71        self.lookup_intern(db).format(db)
72    }
73
74    /// Returns [Maybe::Err] if the type is [TypeLongId::Missing].
75    pub fn check_not_missing(&self, db: &dyn SemanticGroup) -> Maybe<()> {
76        if let TypeLongId::Missing(diag_added) = self.lookup_intern(db) {
77            Err(diag_added)
78        } else {
79            Ok(())
80        }
81    }
82
83    /// Returns `true` if the type is [TypeLongId::Missing].
84    pub fn is_missing(&self, db: &dyn SemanticGroup) -> bool {
85        self.check_not_missing(db).is_err()
86    }
87
88    /// Returns `true` if the type is `()`.
89    pub fn is_unit(&self, db: &dyn SemanticGroup) -> bool {
90        matches!(self.lookup_intern(db), TypeLongId::Tuple(types) if types.is_empty())
91    }
92
93    /// Returns the [TypeHead] for a type if available.
94    pub fn head(&self, db: &dyn SemanticGroup) -> Option<TypeHead> {
95        self.lookup_intern(db).head(db)
96    }
97
98    /// Returns true if the type does not depend on any generics.
99    pub fn is_fully_concrete(&self, db: &dyn SemanticGroup) -> bool {
100        db.priv_type_is_fully_concrete(*self)
101    }
102
103    /// Returns true if the type does not contain any inference variables.
104    pub fn is_var_free(&self, db: &dyn SemanticGroup) -> bool {
105        db.priv_type_is_var_free(*self)
106    }
107
108    /// Returns whether the type is phantom.
109    /// Type is considered phantom if it has the `#[phantom]` attribute, or is a tuple or fixed
110    /// sized array containing it.
111    pub fn is_phantom(&self, db: &dyn SemanticGroup) -> bool {
112        self.lookup_intern(db).is_phantom(db)
113    }
114
115    /// Short name of the type argument.
116    pub fn short_name(&self, db: &dyn SemanticGroup) -> String {
117        db.priv_type_short_name(*self)
118    }
119}
120impl TypeLongId {
121    pub fn format(&self, db: &dyn SemanticGroup) -> String {
122        format!("{:?}", self.debug(db.elongate()))
123    }
124
125    /// Returns the [TypeHead] for a type if available.
126    pub fn head(&self, db: &dyn SemanticGroup) -> Option<TypeHead> {
127        Some(match self {
128            TypeLongId::Concrete(concrete) => TypeHead::Concrete(concrete.generic_type(db)),
129            TypeLongId::Tuple(_) => TypeHead::Tuple,
130            TypeLongId::Snapshot(inner) => TypeHead::Snapshot(Box::new(inner.head(db)?)),
131            TypeLongId::Coupon(_) => TypeHead::Coupon,
132            TypeLongId::FixedSizeArray { .. } => TypeHead::FixedSizeArray,
133            TypeLongId::GenericParameter(_)
134            | TypeLongId::Var(_)
135            | TypeLongId::Missing(_)
136            | TypeLongId::ImplType(_)
137            | TypeLongId::TraitType(_)
138            | TypeLongId::Closure(_) => {
139                return None;
140            }
141        })
142    }
143
144    /// Returns whether the type is phantom.
145    /// Type is considered phantom if it has the `#[phantom]` attribute, (or an other attribute
146    /// declared by a plugin as defining a phantom type), or is a tuple or fixed sized array
147    /// containing it.
148    pub fn is_phantom(&self, db: &dyn SemanticGroup) -> bool {
149        let phantom_type_attributes = db.declared_phantom_type_attributes();
150        match self {
151            TypeLongId::Concrete(id) => match id {
152                ConcreteTypeId::Struct(id) => phantom_type_attributes
153                    .iter()
154                    .any(|attr| id.has_attr(db, attr).unwrap_or_default()),
155                ConcreteTypeId::Enum(id) => phantom_type_attributes
156                    .iter()
157                    .any(|attr| id.has_attr(db, attr).unwrap_or_default()),
158                ConcreteTypeId::Extern(id) => phantom_type_attributes
159                    .iter()
160                    .any(|attr| id.has_attr(db, attr).unwrap_or_default()),
161            },
162            TypeLongId::Tuple(inner) => inner.iter().any(|ty| ty.is_phantom(db)),
163            TypeLongId::FixedSizeArray { type_id, .. } => type_id.is_phantom(db),
164            TypeLongId::Snapshot(_)
165            | TypeLongId::GenericParameter(_)
166            | TypeLongId::Var(_)
167            | TypeLongId::Coupon(_)
168            | TypeLongId::TraitType(_)
169            | TypeLongId::ImplType(_)
170            | TypeLongId::Missing(_)
171            | TypeLongId::Closure(_) => false,
172        }
173    }
174}
175impl DebugWithDb<dyn SemanticGroup> for TypeLongId {
176    fn fmt(
177        &self,
178        f: &mut std::fmt::Formatter<'_>,
179        db: &(dyn SemanticGroup + 'static),
180    ) -> std::fmt::Result {
181        let def_db = db.upcast();
182        match self {
183            TypeLongId::Concrete(concrete) => write!(f, "{}", concrete.format(db)),
184            TypeLongId::Tuple(inner_types) => {
185                if inner_types.len() == 1 {
186                    write!(f, "({},)", inner_types[0].format(db))
187                } else {
188                    write!(f, "({})", inner_types.iter().map(|ty| ty.format(db)).join(", "))
189                }
190            }
191            TypeLongId::Snapshot(ty) => write!(f, "@{}", ty.format(db)),
192            TypeLongId::GenericParameter(generic_param) => {
193                write!(f, "{}", generic_param.name(def_db).unwrap_or_else(|| "_".into()))
194            }
195            TypeLongId::ImplType(impl_type_id) => {
196                write!(f, "{}::{}", impl_type_id.impl_id.name(db), impl_type_id.ty.name(def_db))
197            }
198            TypeLongId::Var(var) => write!(f, "?{}", var.id.0),
199            TypeLongId::Coupon(function_id) => write!(f, "{}::Coupon", function_id.full_name(db)),
200            TypeLongId::Missing(_) => write!(f, "<missing>"),
201            TypeLongId::FixedSizeArray { type_id, size } => {
202                write!(f, "[{}; {:?}]", type_id.format(db), size.debug(db.elongate()))
203            }
204            TypeLongId::TraitType(trait_type_id) => {
205                write!(
206                    f,
207                    "{}::{}",
208                    trait_type_id.trait_id(def_db).name(def_db),
209                    trait_type_id.name(def_db)
210                )
211            }
212            TypeLongId::Closure(closure) => {
213                write!(f, "{:?}", closure.debug(db.elongate()))
214            }
215        }
216    }
217}
218
219/// Head of a type.
220///
221/// A type that is not one of {generic param, type variable, impl type} has a head, which represents
222/// the kind of the root node in its type tree. This is used for caching queries for fast lookups
223/// when the type is not completely inferred yet.
224#[derive(Clone, Debug, Hash, PartialEq, Eq)]
225pub enum TypeHead {
226    Concrete(GenericTypeId),
227    Snapshot(Box<TypeHead>),
228    Tuple,
229    Coupon,
230    FixedSizeArray,
231}
232
233#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
234pub enum ConcreteTypeId {
235    Struct(ConcreteStructId),
236    Enum(ConcreteEnumId),
237    Extern(ConcreteExternTypeId),
238}
239impl ConcreteTypeId {
240    pub fn new(
241        db: &dyn SemanticGroup,
242        generic_ty: GenericTypeId,
243        generic_args: Vec<semantic::GenericArgumentId>,
244    ) -> Self {
245        match generic_ty {
246            GenericTypeId::Struct(id) => ConcreteTypeId::Struct(
247                ConcreteStructLongId { struct_id: id, generic_args }.intern(db),
248            ),
249            GenericTypeId::Enum(id) => {
250                ConcreteTypeId::Enum(ConcreteEnumLongId { enum_id: id, generic_args }.intern(db))
251            }
252            GenericTypeId::Extern(id) => ConcreteTypeId::Extern(
253                ConcreteExternTypeLongId { extern_type_id: id, generic_args }.intern(db),
254            ),
255        }
256    }
257    pub fn generic_type(&self, db: &dyn SemanticGroup) -> GenericTypeId {
258        match self {
259            ConcreteTypeId::Struct(id) => GenericTypeId::Struct(id.lookup_intern(db).struct_id),
260            ConcreteTypeId::Enum(id) => GenericTypeId::Enum(id.lookup_intern(db).enum_id),
261            ConcreteTypeId::Extern(id) => {
262                GenericTypeId::Extern(id.lookup_intern(db).extern_type_id)
263            }
264        }
265    }
266    pub fn generic_args(&self, db: &dyn SemanticGroup) -> Vec<semantic::GenericArgumentId> {
267        match self {
268            ConcreteTypeId::Struct(id) => id.lookup_intern(db).generic_args,
269            ConcreteTypeId::Enum(id) => id.lookup_intern(db).generic_args,
270            ConcreteTypeId::Extern(id) => id.lookup_intern(db).generic_args,
271        }
272    }
273    pub fn format(&self, db: &dyn SemanticGroup) -> String {
274        let generic_type_format = self.generic_type(db).format(db.upcast());
275        let mut generic_args = self.generic_args(db).into_iter();
276        if let Some(first) = generic_args.next() {
277            // Soft limit for the number of chars in the formatted type.
278            const CHARS_BOUND: usize = 500;
279            let mut f = generic_type_format;
280            f.push_str("::<");
281            f.push_str(&first.format(db));
282            for arg in generic_args {
283                f.push_str(", ");
284                if f.len() > CHARS_BOUND {
285                    // If the formatted type is becoming too long, add short version of arguments.
286                    f.push_str(&arg.short_name(db));
287                } else {
288                    f.push_str(&arg.format(db));
289                }
290            }
291            f.push('>');
292            f
293        } else {
294            generic_type_format
295        }
296    }
297
298    /// Returns whether the type has the `#[must_use]` attribute.
299    pub fn is_must_use(&self, db: &dyn SemanticGroup) -> Maybe<bool> {
300        match self {
301            ConcreteTypeId::Struct(id) => id.has_attr(db, MUST_USE_ATTR),
302            ConcreteTypeId::Enum(id) => id.has_attr(db, MUST_USE_ATTR),
303            ConcreteTypeId::Extern(id) => id.has_attr(db, MUST_USE_ATTR),
304        }
305    }
306    /// Returns true if the type does not depend on any generics.
307    pub fn is_fully_concrete(&self, db: &dyn SemanticGroup) -> bool {
308        self.generic_args(db)
309            .iter()
310            .all(|generic_argument_id| generic_argument_id.is_fully_concrete(db))
311    }
312    /// Returns true if the type does not contain any inference variables.
313    pub fn is_var_free(&self, db: &dyn SemanticGroup) -> bool {
314        self.generic_args(db).iter().all(|generic_argument_id| generic_argument_id.is_var_free(db))
315    }
316}
317impl DebugWithDb<dyn SemanticGroup> for ConcreteTypeId {
318    fn fmt(
319        &self,
320        f: &mut std::fmt::Formatter<'_>,
321        db: &(dyn SemanticGroup + 'static),
322    ) -> std::fmt::Result {
323        write!(f, "{}", self.format(db))
324    }
325}
326
327#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
328pub struct ConcreteStructLongId {
329    pub struct_id: StructId,
330    pub generic_args: Vec<semantic::GenericArgumentId>,
331}
332define_short_id!(
333    ConcreteStructId,
334    ConcreteStructLongId,
335    SemanticGroup,
336    lookup_intern_concrete_struct,
337    intern_concrete_struct
338);
339semantic_object_for_id!(
340    ConcreteStructId,
341    lookup_intern_concrete_struct,
342    intern_concrete_struct,
343    ConcreteStructLongId
344);
345impl ConcreteStructId {
346    pub fn struct_id(&self, db: &dyn SemanticGroup) -> StructId {
347        self.lookup_intern(db).struct_id
348    }
349}
350impl DebugWithDb<dyn SemanticGroup> for ConcreteStructLongId {
351    fn fmt(
352        &self,
353        f: &mut std::fmt::Formatter<'_>,
354        db: &(dyn SemanticGroup + 'static),
355    ) -> std::fmt::Result {
356        write!(f, "{:?}", ConcreteTypeId::Struct(self.clone().intern(db)).debug(db))
357    }
358}
359
360#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
361pub struct ConcreteEnumLongId {
362    pub enum_id: EnumId,
363    pub generic_args: Vec<semantic::GenericArgumentId>,
364}
365impl DebugWithDb<dyn SemanticGroup> for ConcreteEnumLongId {
366    fn fmt(
367        &self,
368        f: &mut std::fmt::Formatter<'_>,
369        db: &(dyn SemanticGroup + 'static),
370    ) -> std::fmt::Result {
371        write!(f, "{:?}", ConcreteTypeId::Enum(self.clone().intern(db)).debug(db))
372    }
373}
374
375define_short_id!(
376    ConcreteEnumId,
377    ConcreteEnumLongId,
378    SemanticGroup,
379    lookup_intern_concrete_enum,
380    intern_concrete_enum
381);
382semantic_object_for_id!(
383    ConcreteEnumId,
384    lookup_intern_concrete_enum,
385    intern_concrete_enum,
386    ConcreteEnumLongId
387);
388impl ConcreteEnumId {
389    pub fn enum_id(&self, db: &dyn SemanticGroup) -> EnumId {
390        self.lookup_intern(db).enum_id
391    }
392}
393
394#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
395pub struct ConcreteExternTypeLongId {
396    pub extern_type_id: ExternTypeId,
397    pub generic_args: Vec<semantic::GenericArgumentId>,
398}
399define_short_id!(
400    ConcreteExternTypeId,
401    ConcreteExternTypeLongId,
402    SemanticGroup,
403    lookup_intern_concrete_extern_type,
404    intern_concrete_extern_type
405);
406semantic_object_for_id!(
407    ConcreteExternTypeId,
408    lookup_intern_concrete_extern_type,
409    intern_concrete_extern_type,
410    ConcreteExternTypeLongId
411);
412impl ConcreteExternTypeId {
413    pub fn extern_type_id(&self, db: &dyn SemanticGroup) -> ExternTypeId {
414        self.lookup_intern(db).extern_type_id
415    }
416}
417
418/// A type id of a closure function.
419#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
420pub struct ClosureTypeLongId {
421    pub param_tys: Vec<TypeId>,
422    pub ret_ty: TypeId,
423    /// The set of types captured by the closure, this field is used to determined if the
424    /// closure has Drop, Destruct or PanicDestruct.
425    /// A vector as the fields needs to be hashable.
426    pub captured_types: Vec<TypeId>,
427    /// The parent function of the closure or an error.
428    pub parent_function: Maybe<FunctionId>,
429    /// Every closure has a unique type that is based on the stable location of its wrapper.
430    #[dont_rewrite]
431    pub wrapper_location: StableLocation,
432}
433
434impl DebugWithDb<dyn SemanticGroup> for ClosureTypeLongId {
435    fn fmt(
436        &self,
437        f: &mut std::fmt::Formatter<'_>,
438        db: &(dyn SemanticGroup + 'static),
439    ) -> std::fmt::Result {
440        write!(f, "{{closure@{:?}}}", self.wrapper_location.debug(db.upcast()))
441    }
442}
443
444/// An impl item of kind type.
445#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
446pub struct ImplTypeId {
447    /// The impl the item type is in.
448    impl_id: ImplId,
449    /// The trait type this impl type "implements".
450    ty: TraitTypeId,
451}
452impl ImplTypeId {
453    /// Creates a new impl type id. For an impl type of a concrete impl, asserts that the trait
454    /// type belongs to the same trait that the impl implements (panics if not).
455    pub fn new(impl_id: ImplId, ty: TraitTypeId, db: &dyn SemanticGroup) -> Self {
456        if let crate::items::imp::ImplLongId::Concrete(concrete_impl) = impl_id.lookup_intern(db) {
457            let impl_def_id = concrete_impl.impl_def_id(db);
458            assert_eq!(Ok(ty.trait_id(db.upcast())), db.impl_def_trait(impl_def_id));
459        }
460
461        ImplTypeId { impl_id, ty }
462    }
463    pub fn impl_id(&self) -> ImplId {
464        self.impl_id
465    }
466    pub fn ty(&self) -> TraitTypeId {
467        self.ty
468    }
469    pub fn format(&self, db: &dyn SemanticGroup) -> SmolStr {
470        format!("{}::{}", self.impl_id.name(db.upcast()), self.ty.name(db.upcast())).into()
471    }
472}
473impl DebugWithDb<dyn SemanticGroup> for ImplTypeId {
474    fn fmt(
475        &self,
476        f: &mut std::fmt::Formatter<'_>,
477        db: &(dyn SemanticGroup + 'static),
478    ) -> std::fmt::Result {
479        write!(f, "{}", self.format(db))
480    }
481}
482
483/// A wrapper around ImplTypeById that implements Ord for saving in an ordered collection.
484#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
485pub struct ImplTypeById(ImplTypeId);
486
487impl From<ImplTypeId> for ImplTypeById {
488    fn from(impl_type_id: ImplTypeId) -> Self {
489        Self(impl_type_id)
490    }
491}
492impl Ord for ImplTypeById {
493    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
494        self.0
495            .impl_id
496            .get_internal_id()
497            .cmp(other.0.impl_id.get_internal_id())
498            .then_with(|| self.0.ty.get_internal_id().cmp(other.0.ty.get_internal_id()))
499    }
500}
501impl PartialOrd for ImplTypeById {
502    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
503        Some(self.cmp(other))
504    }
505}
506
507// TODO(spapini): add a query wrapper.
508/// Resolves a type given a module and a path. Used for resolving from non-statement context.
509pub fn resolve_type(
510    db: &dyn SemanticGroup,
511    diagnostics: &mut SemanticDiagnostics,
512    resolver: &mut Resolver<'_>,
513    ty_syntax: &ast::Expr,
514) -> TypeId {
515    maybe_resolve_type(db, diagnostics, resolver, ty_syntax, None)
516        .unwrap_or_else(|diag_added| TypeId::missing(db, diag_added))
517}
518/// Resolves a type given a module and a path. `statement_env` should be provided if called from
519/// statement context.
520pub fn resolve_type_with_environment(
521    db: &dyn SemanticGroup,
522    diagnostics: &mut SemanticDiagnostics,
523    resolver: &mut Resolver<'_>,
524    ty_syntax: &ast::Expr,
525    statement_env: Option<&mut Environment>,
526) -> TypeId {
527    maybe_resolve_type(db, diagnostics, resolver, ty_syntax, statement_env)
528        .unwrap_or_else(|diag_added| TypeId::missing(db, diag_added))
529}
530pub fn maybe_resolve_type(
531    db: &dyn SemanticGroup,
532    diagnostics: &mut SemanticDiagnostics,
533    resolver: &mut Resolver<'_>,
534    ty_syntax: &ast::Expr,
535    mut statement_env: Option<&mut Environment>,
536) -> Maybe<TypeId> {
537    let syntax_db = db.upcast();
538    Ok(match ty_syntax {
539        ast::Expr::Path(path) => {
540            match resolver.resolve_concrete_path_ex(
541                diagnostics,
542                path,
543                NotFoundItemType::Type,
544                statement_env,
545            )? {
546                ResolvedConcreteItem::Type(ty) => ty,
547                _ => {
548                    return Err(diagnostics.report(path, NotAType));
549                }
550            }
551        }
552        ast::Expr::Parenthesized(expr_syntax) => resolve_type_with_environment(
553            db,
554            diagnostics,
555            resolver,
556            &expr_syntax.expr(syntax_db),
557            statement_env,
558        ),
559        ast::Expr::Tuple(tuple_syntax) => {
560            let sub_tys = tuple_syntax
561                .expressions(syntax_db)
562                .elements(syntax_db)
563                .into_iter()
564                .map(|subexpr_syntax| {
565                    resolve_type_with_environment(
566                        db,
567                        diagnostics,
568                        resolver,
569                        &subexpr_syntax,
570                        statement_env.as_deref_mut(),
571                    )
572                })
573                .collect();
574            TypeLongId::Tuple(sub_tys).intern(db)
575        }
576        ast::Expr::Unary(unary_syntax)
577            if matches!(unary_syntax.op(syntax_db), ast::UnaryOperator::At(_)) =>
578        {
579            let ty = resolve_type_with_environment(
580                db,
581                diagnostics,
582                resolver,
583                &unary_syntax.expr(syntax_db),
584                statement_env,
585            );
586            TypeLongId::Snapshot(ty).intern(db)
587        }
588        ast::Expr::Unary(unary_syntax)
589            if matches!(unary_syntax.op(syntax_db), ast::UnaryOperator::Desnap(_)) =>
590        {
591            let ty = resolve_type_with_environment(
592                db,
593                diagnostics,
594                resolver,
595                &unary_syntax.expr(syntax_db),
596                statement_env,
597            );
598            if let Some(desnapped_ty) =
599                try_extract_matches!(ty.lookup_intern(db), TypeLongId::Snapshot)
600            {
601                desnapped_ty
602            } else {
603                return Err(diagnostics.report(ty_syntax, DesnapNonSnapshot));
604            }
605        }
606        ast::Expr::FixedSizeArray(array_syntax) => {
607            let [ty] = &array_syntax.exprs(syntax_db).elements(syntax_db)[..] else {
608                return Err(diagnostics.report(ty_syntax, FixedSizeArrayTypeNonSingleType));
609            };
610            let ty = resolve_type_with_environment(db, diagnostics, resolver, ty, statement_env);
611            let size = match extract_fixed_size_array_size(db, diagnostics, array_syntax, resolver)?
612            {
613                Some(size) => size,
614                None => {
615                    return Err(diagnostics.report(ty_syntax, FixedSizeArrayTypeEmptySize));
616                }
617            };
618            TypeLongId::FixedSizeArray { type_id: ty, size }.intern(db)
619        }
620        _ => {
621            return Err(diagnostics.report(ty_syntax, UnknownType));
622        }
623    })
624}
625
626/// Extracts the size of a fixed size array, or none if the size is missing. Reports an error if the
627/// size is not a numeric literal.
628pub fn extract_fixed_size_array_size(
629    db: &dyn SemanticGroup,
630    diagnostics: &mut SemanticDiagnostics,
631    syntax: &ast::ExprFixedSizeArray,
632    resolver: &Resolver<'_>,
633) -> Maybe<Option<ConstValueId>> {
634    let syntax_db = db.upcast();
635    match syntax.size(syntax_db) {
636        ast::OptionFixedSizeArraySize::FixedSizeArraySize(size_clause) => {
637            let environment = Environment::empty();
638            let resolver = Resolver::with_data(
639                db,
640                (resolver.data).clone_with_inference_id(db, resolver.inference_data.inference_id),
641            );
642            let mut ctx = ComputationContext::new(
643                db,
644                diagnostics,
645                resolver,
646                None,
647                environment,
648                ContextFunction::Global,
649            );
650            let size_expr_syntax = size_clause.size(syntax_db);
651            let size = compute_expr_semantic(&mut ctx, &size_expr_syntax);
652            let const_value = resolve_const_expr_and_evaluate(
653                db,
654                &mut ctx,
655                &size,
656                size_expr_syntax.stable_ptr().untyped(),
657                get_usize_ty(db),
658            );
659            if matches!(const_value, ConstValue::Int(_, _) | ConstValue::Generic(_)) {
660                Ok(Some(const_value.intern(db)))
661            } else {
662                Err(diagnostics.report(syntax, FixedSizeArrayNonNumericSize))
663            }
664        }
665        ast::OptionFixedSizeArraySize::Empty(_) => Ok(None),
666    }
667}
668
669/// Verifies that a given fixed size array size is within limits, and adds a diagnostic if not.
670pub fn verify_fixed_size_array_size(
671    diagnostics: &mut SemanticDiagnostics,
672    size: &BigInt,
673    syntax: &ast::ExprFixedSizeArray,
674) -> Maybe<()> {
675    if size > &BigInt::from(i16::MAX) {
676        return Err(diagnostics.report(syntax, FixedSizeArraySizeTooBig));
677    }
678    Ok(())
679}
680
681/// Query implementation of [crate::db::SemanticGroup::generic_type_generic_params].
682pub fn generic_type_generic_params(
683    db: &dyn SemanticGroup,
684    generic_type: GenericTypeId,
685) -> Maybe<Vec<semantic::GenericParam>> {
686    match generic_type {
687        GenericTypeId::Struct(id) => db.struct_generic_params(id),
688        GenericTypeId::Enum(id) => db.enum_generic_params(id),
689        GenericTypeId::Extern(id) => db.extern_type_declaration_generic_params(id),
690    }
691}
692
693#[derive(Clone, Debug, PartialEq, Eq)]
694pub struct TypeInfo {
695    pub droppable: Result<ImplId, InferenceError>,
696    pub copyable: Result<ImplId, InferenceError>,
697    pub destruct_impl: Result<ImplId, InferenceError>,
698    pub panic_destruct_impl: Result<ImplId, InferenceError>,
699}
700
701/// Checks if there is at least one impl that can be inferred for a specific concrete trait.
702pub fn get_impl_at_context(
703    db: &dyn SemanticGroup,
704    lookup_context: ImplLookupContext,
705    concrete_trait_id: ConcreteTraitId,
706    stable_ptr: Option<SyntaxStablePtrId>,
707) -> Result<ImplId, InferenceError> {
708    let mut inference_data = InferenceData::new(InferenceId::NoContext);
709    let mut inference = inference_data.inference(db);
710    let constrains = db.generic_params_type_constraints(lookup_context.generic_params.clone());
711    inference.conform_generic_params_type_constraints(&constrains);
712    // It's ok to consume the errors without reporting as this is a helper function meant to find an
713    // impl and return it, but it's ok if the impl can't be found.
714    let impl_id = inference.new_impl_var(concrete_trait_id, stable_ptr, lookup_context);
715    if let Err((err_set, _)) = inference.finalize_without_reporting() {
716        return Err(inference
717            .consume_error_without_reporting(err_set)
718            .expect("Error couldn't be already consumed"));
719    };
720    Ok(inference.rewrite(impl_id).no_err())
721}
722
723/// Query implementation of [crate::db::SemanticGroup::single_value_type].
724pub fn single_value_type(db: &dyn SemanticGroup, ty: TypeId) -> Maybe<bool> {
725    Ok(match ty.lookup_intern(db) {
726        TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
727            ConcreteTypeId::Struct(id) => {
728                for member in db.struct_members(id.struct_id(db))?.values() {
729                    if !db.single_value_type(member.ty)? {
730                        return Ok(false);
731                    }
732                }
733                true
734            }
735            ConcreteTypeId::Enum(id) => {
736                let variants = db.enum_variants(id.enum_id(db))?;
737                if variants.len() != 1 {
738                    return Ok(false);
739                }
740
741                db.single_value_type(
742                    db.variant_semantic(id.enum_id(db), *variants.values().next().unwrap())?.ty,
743                )?
744            }
745            ConcreteTypeId::Extern(_) => false,
746        },
747        TypeLongId::Tuple(types) => {
748            for ty in &types {
749                if !db.single_value_type(*ty)? {
750                    return Ok(false);
751                }
752            }
753            true
754        }
755        TypeLongId::Snapshot(ty) => db.single_value_type(ty)?,
756        TypeLongId::GenericParameter(_)
757        | TypeLongId::Var(_)
758        | TypeLongId::Missing(_)
759        | TypeLongId::Coupon(_)
760        | TypeLongId::ImplType(_)
761        | TypeLongId::TraitType(_)
762        | TypeLongId::Closure(_) => false,
763        TypeLongId::FixedSizeArray { type_id, size } => {
764            db.single_value_type(type_id)?
765                || matches!(size.lookup_intern(db),
766                            ConstValue::Int(value, _) if value.is_zero())
767        }
768    })
769}
770
771/// Adds diagnostics for a type, post semantic analysis of types.
772pub fn add_type_based_diagnostics(
773    db: &dyn SemanticGroup,
774    diagnostics: &mut SemanticDiagnostics,
775    ty: TypeId,
776    stable_ptr: impl Into<SyntaxStablePtrId> + Copy,
777) {
778    if db.type_size_info(ty) == Ok(TypeSizeInformation::Infinite) {
779        diagnostics.report(stable_ptr, InfiniteSizeType(ty));
780    }
781    if let TypeLongId::Concrete(ConcreteTypeId::Extern(extrn)) = ty.lookup_intern(db) {
782        let long_id = extrn.lookup_intern(db);
783        if long_id.extern_type_id.name(db.upcast()).as_str() == "Array" {
784            if let [GenericArgumentId::Type(arg_ty)] = &long_id.generic_args[..] {
785                if db.type_size_info(*arg_ty) == Ok(TypeSizeInformation::ZeroSized) {
786                    diagnostics.report(stable_ptr, ArrayOfZeroSizedElements(*arg_ty));
787                }
788            }
789        }
790    }
791}
792
793#[derive(Clone, Debug, PartialEq, Eq)]
794pub enum TypeSizeInformation {
795    /// The type has an infinite size - caused by a recursion in it.
796    /// If the type simply holds an infinite type, it would be considered `Other`, for diagnostics
797    /// reasons.
798    Infinite,
799    /// The type is zero size.
800    ZeroSized,
801    /// The typed has some none zero size.
802    Other,
803}
804
805/// Query implementation of [crate::db::SemanticGroup::type_size_info].
806pub fn type_size_info(db: &dyn SemanticGroup, ty: TypeId) -> Maybe<TypeSizeInformation> {
807    match ty.lookup_intern(db) {
808        TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
809            ConcreteTypeId::Struct(id) => {
810                let mut zero_sized = true;
811                for (_, member) in db.struct_members(id.struct_id(db))?.iter() {
812                    if db.type_size_info(member.ty)? != TypeSizeInformation::ZeroSized {
813                        zero_sized = false;
814                    }
815                }
816                if zero_sized {
817                    return Ok(TypeSizeInformation::ZeroSized);
818                }
819            }
820            ConcreteTypeId::Enum(id) => {
821                for (_, variant) in db.enum_variants(id.enum_id(db))? {
822                    // Recursive calling in order to find infinite sized types.
823                    db.type_size_info(db.variant_semantic(id.enum_id(db), variant)?.ty)?;
824                }
825            }
826            ConcreteTypeId::Extern(_) => {}
827        },
828        TypeLongId::Tuple(types) => {
829            let mut zero_sized = true;
830            for ty in types {
831                if db.type_size_info(ty)? != TypeSizeInformation::ZeroSized {
832                    zero_sized = false;
833                }
834            }
835            if zero_sized {
836                return Ok(TypeSizeInformation::ZeroSized);
837            }
838        }
839        TypeLongId::Snapshot(ty) => {
840            if db.type_size_info(ty)? == TypeSizeInformation::ZeroSized {
841                return Ok(TypeSizeInformation::ZeroSized);
842            }
843        }
844        TypeLongId::Coupon(_) => return Ok(TypeSizeInformation::ZeroSized),
845        TypeLongId::GenericParameter(_)
846        | TypeLongId::Var(_)
847        | TypeLongId::Missing(_)
848        | TypeLongId::TraitType(_)
849        | TypeLongId::ImplType(_)
850        | TypeLongId::Closure(_) => {}
851        TypeLongId::FixedSizeArray { type_id, size } => {
852            if matches!(size.lookup_intern(db), ConstValue::Int(value,_) if value.is_zero())
853                || db.type_size_info(type_id)? == TypeSizeInformation::ZeroSized
854            {
855                return Ok(TypeSizeInformation::ZeroSized);
856            }
857        }
858    }
859    Ok(TypeSizeInformation::Other)
860}
861
862/// Cycle handling of [crate::db::SemanticGroup::type_size_info].
863pub fn type_size_info_cycle(
864    _db: &dyn SemanticGroup,
865    _cycle: &salsa::Cycle,
866    _ty: &TypeId,
867) -> Maybe<TypeSizeInformation> {
868    Ok(TypeSizeInformation::Infinite)
869}
870
871// TODO(spapini): type info lookup for non generic types needs to not depend on lookup_context.
872// This is to ensure that sierra generator will see a consistent type info of types.
873/// Query implementation of [crate::db::SemanticGroup::type_info].
874pub fn type_info(
875    db: &dyn SemanticGroup,
876    lookup_context: ImplLookupContext,
877    ty: TypeId,
878) -> Maybe<TypeInfo> {
879    // Dummy stable pointer for type inference variables, since inference is disabled.
880    let droppable =
881        get_impl_at_context(db, lookup_context.clone(), concrete_drop_trait(db, ty), None);
882    let copyable =
883        get_impl_at_context(db, lookup_context.clone(), concrete_copy_trait(db, ty), None);
884    let destruct_impl =
885        get_impl_at_context(db, lookup_context.clone(), concrete_destruct_trait(db, ty), None);
886    let panic_destruct_impl =
887        get_impl_at_context(db, lookup_context, concrete_panic_destruct_trait(db, ty), None);
888    Ok(TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl })
889}
890
891pub fn priv_type_is_fully_concrete(db: &dyn SemanticGroup, ty: TypeId) -> bool {
892    match ty.lookup_intern(db) {
893        TypeLongId::Concrete(concrete_type_id) => concrete_type_id.is_fully_concrete(db),
894        TypeLongId::Tuple(types) => types.iter().all(|ty| ty.is_fully_concrete(db)),
895        TypeLongId::Snapshot(ty) => ty.is_fully_concrete(db),
896        TypeLongId::GenericParameter(_)
897        | TypeLongId::Var(_)
898        | TypeLongId::Missing(_)
899        | TypeLongId::ImplType(_)
900        | TypeLongId::TraitType(_) => false,
901        TypeLongId::Coupon(function_id) => function_id.is_fully_concrete(db),
902        TypeLongId::FixedSizeArray { type_id, size } => {
903            type_id.is_fully_concrete(db) && size.is_fully_concrete(db)
904        }
905        TypeLongId::Closure(closure) => {
906            closure.param_tys.iter().all(|param| param.is_fully_concrete(db))
907                && closure.ret_ty.is_fully_concrete(db)
908        }
909    }
910}
911
912pub fn priv_type_is_var_free(db: &dyn SemanticGroup, ty: TypeId) -> bool {
913    match ty.lookup_intern(db) {
914        TypeLongId::Concrete(concrete_type_id) => concrete_type_id.is_var_free(db),
915        TypeLongId::Tuple(types) => types.iter().all(|ty| ty.is_var_free(db)),
916        TypeLongId::Snapshot(ty) => ty.is_var_free(db),
917        TypeLongId::Var(_) => false,
918        TypeLongId::GenericParameter(_) | TypeLongId::Missing(_) | TypeLongId::TraitType(_) => true,
919        TypeLongId::Coupon(function_id) => function_id.is_var_free(db),
920        TypeLongId::FixedSizeArray { type_id, size } => {
921            type_id.is_var_free(db) && size.is_var_free(db)
922        }
923        // TODO(TomerStarkware): consider rename the function to `priv_type_might_need_rewrite`.
924        // a var free ImplType needs to be rewritten if has impl bounds constraints.
925        TypeLongId::ImplType(_) => false,
926        TypeLongId::Closure(closure) => {
927            closure.param_tys.iter().all(|param| param.is_var_free(db))
928                && closure.ret_ty.is_var_free(db)
929        }
930    }
931}
932
933pub fn priv_type_short_name(db: &dyn SemanticGroup, ty: TypeId) -> String {
934    match ty.lookup_intern(db) {
935        TypeLongId::Concrete(concrete_type_id) => {
936            let mut result = concrete_type_id.generic_type(db).format(db.upcast());
937            let mut generic_args = concrete_type_id.generic_args(db).into_iter().peekable();
938            if generic_args.peek().is_some() {
939                result.push_str("::<h0x");
940                let mut hasher = Keccak256::new();
941                for arg in generic_args {
942                    hasher.update(arg.short_name(db).as_bytes());
943                }
944                for c in hasher.finalize() {
945                    result.push_str(&format!("{c:x}"));
946                }
947                result.push('>');
948            }
949            result
950        }
951        TypeLongId::Tuple(types) => {
952            let mut result = String::from("(h0x");
953            let mut hasher = Keccak256::new();
954            for ty in types {
955                hasher.update(ty.short_name(db).as_bytes());
956            }
957            for c in hasher.finalize() {
958                result.push_str(&format!("{c:x}"));
959            }
960            result.push(')');
961            result
962        }
963        TypeLongId::Snapshot(ty) => {
964            format!("@{}", ty.short_name(db))
965        }
966        TypeLongId::FixedSizeArray { type_id, size } => {
967            format!("[{}; {:?}", type_id.short_name(db), size.debug(db.elongate()))
968        }
969        other => other.format(db),
970    }
971}
972
973/// Peels all wrapping Snapshot (`@`) from the type.
974/// Returns the number of peeled snapshots and the inner type.
975pub fn peel_snapshots(db: &dyn SemanticGroup, ty: TypeId) -> (usize, TypeLongId) {
976    peel_snapshots_ex(db, ty.lookup_intern(db))
977}
978
979/// Same as `peel_snapshots`, but takes a `TypeLongId` instead of a `TypeId`.
980pub fn peel_snapshots_ex(db: &dyn SemanticGroup, mut long_ty: TypeLongId) -> (usize, TypeLongId) {
981    let mut n_snapshots = 0;
982    while let TypeLongId::Snapshot(ty) = long_ty {
983        long_ty = ty.lookup_intern(db);
984        n_snapshots += 1;
985    }
986    (n_snapshots, long_ty)
987}
988
989/// Wraps a type with Snapshot (`@`) `n_snapshots` times.
990pub fn wrap_in_snapshots(db: &dyn SemanticGroup, mut ty: TypeId, n_snapshots: usize) -> TypeId {
991    for _ in 0..n_snapshots {
992        ty = TypeLongId::Snapshot(ty).intern(db);
993    }
994    ty
995}
996
997/// Returns `true` if coupons are enabled in the module.
998pub(crate) fn are_coupons_enabled(db: &dyn SemanticGroup, module_file_id: ModuleFileId) -> bool {
999    let owning_crate = module_file_id.0.owning_crate(db.upcast());
1000    let Some(config) = db.crate_config(owning_crate) else { return false };
1001    config.settings.experimental_features.coupons
1002}