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