cairo_lang_semantic/
types.rs

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