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 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 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 pub fn is_missing(&self, db: &dyn SemanticGroup) -> bool {
84 self.check_not_missing(db).is_err()
85 }
86
87 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 pub fn head(&self, db: &dyn SemanticGroup) -> Option<TypeHead> {
94 self.lookup_intern(db).head(db)
95 }
96
97 pub fn is_fully_concrete(&self, db: &dyn SemanticGroup) -> bool {
99 db.priv_type_is_fully_concrete(*self)
100 }
101
102 pub fn is_var_free(&self, db: &dyn SemanticGroup) -> bool {
104 db.priv_type_is_var_free(*self)
105 }
106
107 pub fn is_phantom(&self, db: &dyn SemanticGroup) -> bool {
111 self.lookup_intern(db).is_phantom(db)
112 }
113
114 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 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 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 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#[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 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 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 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 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 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#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
462pub struct ClosureTypeLongId {
463 pub param_tys: Vec<TypeId>,
464 pub ret_ty: TypeId,
465 pub captured_types: Vec<TypeId>,
469 pub parent_function: Maybe<FunctionId>,
471 #[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#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
488pub struct ImplTypeId {
489 impl_id: ImplId,
491 ty: TraitTypeId,
493}
494impl ImplTypeId {
495 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#[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
549pub 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}
560pub 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
668pub 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
712pub 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
724pub 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
744pub 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 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
766pub 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
813pub 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 Infinite,
841 ZeroSized,
843 Other,
845}
846
847pub 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 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
903pub 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
912pub fn type_info(
916 db: &dyn SemanticGroup,
917 lookup_context: ImplLookupContext,
918 ty: TypeId,
919) -> Maybe<TypeInfo> {
920 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 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
1013pub fn peel_snapshots(db: &dyn SemanticGroup, ty: TypeId) -> (usize, TypeLongId) {
1016 peel_snapshots_ex(db, ty.lookup_intern(db))
1017}
1018
1019pub 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
1029pub 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
1037pub(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}