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