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