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