1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_defs::diagnostic_utils::StableLocation;
3use cairo_lang_defs::ids::{
4 EnumId, ExternTypeId, GenericParamId, GenericTypeId, ModuleFileId, NamedLanguageElementId,
5 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;
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 TraitType(TraitTypeId),
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::TraitType(_)
138 | TypeLongId::Closure(_) => {
139 return None;
140 }
141 })
142 }
143
144 pub fn is_phantom(&self, db: &dyn SemanticGroup) -> bool {
149 let phantom_type_attributes = db.declared_phantom_type_attributes();
150 match self {
151 TypeLongId::Concrete(id) => match id {
152 ConcreteTypeId::Struct(id) => phantom_type_attributes
153 .iter()
154 .any(|attr| id.has_attr(db, attr).unwrap_or_default()),
155 ConcreteTypeId::Enum(id) => phantom_type_attributes
156 .iter()
157 .any(|attr| id.has_attr(db, attr).unwrap_or_default()),
158 ConcreteTypeId::Extern(id) => phantom_type_attributes
159 .iter()
160 .any(|attr| id.has_attr(db, attr).unwrap_or_default()),
161 },
162 TypeLongId::Tuple(inner) => inner.iter().any(|ty| ty.is_phantom(db)),
163 TypeLongId::FixedSizeArray { type_id, .. } => type_id.is_phantom(db),
164 TypeLongId::Snapshot(_)
165 | TypeLongId::GenericParameter(_)
166 | TypeLongId::Var(_)
167 | TypeLongId::Coupon(_)
168 | TypeLongId::TraitType(_)
169 | TypeLongId::ImplType(_)
170 | TypeLongId::Missing(_)
171 | TypeLongId::Closure(_) => false,
172 }
173 }
174}
175impl DebugWithDb<dyn SemanticGroup> for TypeLongId {
176 fn fmt(
177 &self,
178 f: &mut std::fmt::Formatter<'_>,
179 db: &(dyn SemanticGroup + 'static),
180 ) -> std::fmt::Result {
181 let def_db = db.upcast();
182 match self {
183 TypeLongId::Concrete(concrete) => write!(f, "{}", concrete.format(db)),
184 TypeLongId::Tuple(inner_types) => {
185 if inner_types.len() == 1 {
186 write!(f, "({},)", inner_types[0].format(db))
187 } else {
188 write!(f, "({})", inner_types.iter().map(|ty| ty.format(db)).join(", "))
189 }
190 }
191 TypeLongId::Snapshot(ty) => write!(f, "@{}", ty.format(db)),
192 TypeLongId::GenericParameter(generic_param) => {
193 write!(f, "{}", generic_param.name(def_db).unwrap_or_else(|| "_".into()))
194 }
195 TypeLongId::ImplType(impl_type_id) => {
196 write!(f, "{}::{}", impl_type_id.impl_id.name(db), impl_type_id.ty.name(def_db))
197 }
198 TypeLongId::Var(var) => write!(f, "?{}", var.id.0),
199 TypeLongId::Coupon(function_id) => write!(f, "{}::Coupon", function_id.full_name(db)),
200 TypeLongId::Missing(_) => write!(f, "<missing>"),
201 TypeLongId::FixedSizeArray { type_id, size } => {
202 write!(f, "[{}; {:?}]", type_id.format(db), size.debug(db.elongate()))
203 }
204 TypeLongId::TraitType(trait_type_id) => {
205 write!(
206 f,
207 "{}::{}",
208 trait_type_id.trait_id(def_db).name(def_db),
209 trait_type_id.name(def_db)
210 )
211 }
212 TypeLongId::Closure(closure) => {
213 write!(f, "{:?}", closure.debug(db.elongate()))
214 }
215 }
216 }
217}
218
219#[derive(Clone, Debug, Hash, PartialEq, Eq)]
225pub enum TypeHead {
226 Concrete(GenericTypeId),
227 Snapshot(Box<TypeHead>),
228 Tuple,
229 Coupon,
230 FixedSizeArray,
231}
232
233#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
234pub enum ConcreteTypeId {
235 Struct(ConcreteStructId),
236 Enum(ConcreteEnumId),
237 Extern(ConcreteExternTypeId),
238}
239impl ConcreteTypeId {
240 pub fn new(
241 db: &dyn SemanticGroup,
242 generic_ty: GenericTypeId,
243 generic_args: Vec<semantic::GenericArgumentId>,
244 ) -> Self {
245 match generic_ty {
246 GenericTypeId::Struct(id) => ConcreteTypeId::Struct(
247 ConcreteStructLongId { struct_id: id, generic_args }.intern(db),
248 ),
249 GenericTypeId::Enum(id) => {
250 ConcreteTypeId::Enum(ConcreteEnumLongId { enum_id: id, generic_args }.intern(db))
251 }
252 GenericTypeId::Extern(id) => ConcreteTypeId::Extern(
253 ConcreteExternTypeLongId { extern_type_id: id, generic_args }.intern(db),
254 ),
255 }
256 }
257 pub fn generic_type(&self, db: &dyn SemanticGroup) -> GenericTypeId {
258 match self {
259 ConcreteTypeId::Struct(id) => GenericTypeId::Struct(id.lookup_intern(db).struct_id),
260 ConcreteTypeId::Enum(id) => GenericTypeId::Enum(id.lookup_intern(db).enum_id),
261 ConcreteTypeId::Extern(id) => {
262 GenericTypeId::Extern(id.lookup_intern(db).extern_type_id)
263 }
264 }
265 }
266 pub fn generic_args(&self, db: &dyn SemanticGroup) -> Vec<semantic::GenericArgumentId> {
267 match self {
268 ConcreteTypeId::Struct(id) => id.lookup_intern(db).generic_args,
269 ConcreteTypeId::Enum(id) => id.lookup_intern(db).generic_args,
270 ConcreteTypeId::Extern(id) => id.lookup_intern(db).generic_args,
271 }
272 }
273 pub fn format(&self, db: &dyn SemanticGroup) -> String {
274 let generic_type_format = self.generic_type(db).format(db.upcast());
275 let mut generic_args = self.generic_args(db).into_iter();
276 if let Some(first) = generic_args.next() {
277 const CHARS_BOUND: usize = 500;
279 let mut f = generic_type_format;
280 f.push_str("::<");
281 f.push_str(&first.format(db));
282 for arg in generic_args {
283 f.push_str(", ");
284 if f.len() > CHARS_BOUND {
285 f.push_str(&arg.short_name(db));
287 } else {
288 f.push_str(&arg.format(db));
289 }
290 }
291 f.push('>');
292 f
293 } else {
294 generic_type_format
295 }
296 }
297
298 pub fn is_must_use(&self, db: &dyn SemanticGroup) -> Maybe<bool> {
300 match self {
301 ConcreteTypeId::Struct(id) => id.has_attr(db, MUST_USE_ATTR),
302 ConcreteTypeId::Enum(id) => id.has_attr(db, MUST_USE_ATTR),
303 ConcreteTypeId::Extern(id) => id.has_attr(db, MUST_USE_ATTR),
304 }
305 }
306 pub fn is_fully_concrete(&self, db: &dyn SemanticGroup) -> bool {
308 self.generic_args(db)
309 .iter()
310 .all(|generic_argument_id| generic_argument_id.is_fully_concrete(db))
311 }
312 pub fn is_var_free(&self, db: &dyn SemanticGroup) -> bool {
314 self.generic_args(db).iter().all(|generic_argument_id| generic_argument_id.is_var_free(db))
315 }
316}
317impl DebugWithDb<dyn SemanticGroup> for ConcreteTypeId {
318 fn fmt(
319 &self,
320 f: &mut std::fmt::Formatter<'_>,
321 db: &(dyn SemanticGroup + 'static),
322 ) -> std::fmt::Result {
323 write!(f, "{}", self.format(db))
324 }
325}
326
327#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
328pub struct ConcreteStructLongId {
329 pub struct_id: StructId,
330 pub generic_args: Vec<semantic::GenericArgumentId>,
331}
332define_short_id!(
333 ConcreteStructId,
334 ConcreteStructLongId,
335 SemanticGroup,
336 lookup_intern_concrete_struct,
337 intern_concrete_struct
338);
339semantic_object_for_id!(
340 ConcreteStructId,
341 lookup_intern_concrete_struct,
342 intern_concrete_struct,
343 ConcreteStructLongId
344);
345impl ConcreteStructId {
346 pub fn struct_id(&self, db: &dyn SemanticGroup) -> StructId {
347 self.lookup_intern(db).struct_id
348 }
349}
350impl DebugWithDb<dyn SemanticGroup> for ConcreteStructLongId {
351 fn fmt(
352 &self,
353 f: &mut std::fmt::Formatter<'_>,
354 db: &(dyn SemanticGroup + 'static),
355 ) -> std::fmt::Result {
356 write!(f, "{:?}", ConcreteTypeId::Struct(self.clone().intern(db)).debug(db))
357 }
358}
359
360#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
361pub struct ConcreteEnumLongId {
362 pub enum_id: EnumId,
363 pub generic_args: Vec<semantic::GenericArgumentId>,
364}
365impl DebugWithDb<dyn SemanticGroup> for ConcreteEnumLongId {
366 fn fmt(
367 &self,
368 f: &mut std::fmt::Formatter<'_>,
369 db: &(dyn SemanticGroup + 'static),
370 ) -> std::fmt::Result {
371 write!(f, "{:?}", ConcreteTypeId::Enum(self.clone().intern(db)).debug(db))
372 }
373}
374
375define_short_id!(
376 ConcreteEnumId,
377 ConcreteEnumLongId,
378 SemanticGroup,
379 lookup_intern_concrete_enum,
380 intern_concrete_enum
381);
382semantic_object_for_id!(
383 ConcreteEnumId,
384 lookup_intern_concrete_enum,
385 intern_concrete_enum,
386 ConcreteEnumLongId
387);
388impl ConcreteEnumId {
389 pub fn enum_id(&self, db: &dyn SemanticGroup) -> EnumId {
390 self.lookup_intern(db).enum_id
391 }
392}
393
394#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
395pub struct ConcreteExternTypeLongId {
396 pub extern_type_id: ExternTypeId,
397 pub generic_args: Vec<semantic::GenericArgumentId>,
398}
399define_short_id!(
400 ConcreteExternTypeId,
401 ConcreteExternTypeLongId,
402 SemanticGroup,
403 lookup_intern_concrete_extern_type,
404 intern_concrete_extern_type
405);
406semantic_object_for_id!(
407 ConcreteExternTypeId,
408 lookup_intern_concrete_extern_type,
409 intern_concrete_extern_type,
410 ConcreteExternTypeLongId
411);
412impl ConcreteExternTypeId {
413 pub fn extern_type_id(&self, db: &dyn SemanticGroup) -> ExternTypeId {
414 self.lookup_intern(db).extern_type_id
415 }
416}
417
418#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
420pub struct ClosureTypeLongId {
421 pub param_tys: Vec<TypeId>,
422 pub ret_ty: TypeId,
423 pub captured_types: Vec<TypeId>,
427 pub parent_function: Maybe<FunctionId>,
429 #[dont_rewrite]
431 pub wrapper_location: StableLocation,
432}
433
434impl DebugWithDb<dyn SemanticGroup> for ClosureTypeLongId {
435 fn fmt(
436 &self,
437 f: &mut std::fmt::Formatter<'_>,
438 db: &(dyn SemanticGroup + 'static),
439 ) -> std::fmt::Result {
440 write!(f, "{{closure@{:?}}}", self.wrapper_location.debug(db.upcast()))
441 }
442}
443
444#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
446pub struct ImplTypeId {
447 impl_id: ImplId,
449 ty: TraitTypeId,
451}
452impl ImplTypeId {
453 pub fn new(impl_id: ImplId, ty: TraitTypeId, db: &dyn SemanticGroup) -> Self {
456 if let crate::items::imp::ImplLongId::Concrete(concrete_impl) = impl_id.lookup_intern(db) {
457 let impl_def_id = concrete_impl.impl_def_id(db);
458 assert_eq!(Ok(ty.trait_id(db.upcast())), db.impl_def_trait(impl_def_id));
459 }
460
461 ImplTypeId { impl_id, ty }
462 }
463 pub fn impl_id(&self) -> ImplId {
464 self.impl_id
465 }
466 pub fn ty(&self) -> TraitTypeId {
467 self.ty
468 }
469 pub fn format(&self, db: &dyn SemanticGroup) -> SmolStr {
470 format!("{}::{}", self.impl_id.name(db.upcast()), self.ty.name(db.upcast())).into()
471 }
472}
473impl DebugWithDb<dyn SemanticGroup> for ImplTypeId {
474 fn fmt(
475 &self,
476 f: &mut std::fmt::Formatter<'_>,
477 db: &(dyn SemanticGroup + 'static),
478 ) -> std::fmt::Result {
479 write!(f, "{}", self.format(db))
480 }
481}
482
483#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
485pub struct ImplTypeById(ImplTypeId);
486
487impl From<ImplTypeId> for ImplTypeById {
488 fn from(impl_type_id: ImplTypeId) -> Self {
489 Self(impl_type_id)
490 }
491}
492impl Ord for ImplTypeById {
493 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
494 self.0
495 .impl_id
496 .get_internal_id()
497 .cmp(other.0.impl_id.get_internal_id())
498 .then_with(|| self.0.ty.get_internal_id().cmp(other.0.ty.get_internal_id()))
499 }
500}
501impl PartialOrd for ImplTypeById {
502 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
503 Some(self.cmp(other))
504 }
505}
506
507pub fn resolve_type(
510 db: &dyn SemanticGroup,
511 diagnostics: &mut SemanticDiagnostics,
512 resolver: &mut Resolver<'_>,
513 ty_syntax: &ast::Expr,
514) -> TypeId {
515 maybe_resolve_type(db, diagnostics, resolver, ty_syntax, None)
516 .unwrap_or_else(|diag_added| TypeId::missing(db, diag_added))
517}
518pub fn resolve_type_with_environment(
521 db: &dyn SemanticGroup,
522 diagnostics: &mut SemanticDiagnostics,
523 resolver: &mut Resolver<'_>,
524 ty_syntax: &ast::Expr,
525 statement_env: Option<&mut Environment>,
526) -> TypeId {
527 maybe_resolve_type(db, diagnostics, resolver, ty_syntax, statement_env)
528 .unwrap_or_else(|diag_added| TypeId::missing(db, diag_added))
529}
530pub fn maybe_resolve_type(
531 db: &dyn SemanticGroup,
532 diagnostics: &mut SemanticDiagnostics,
533 resolver: &mut Resolver<'_>,
534 ty_syntax: &ast::Expr,
535 mut statement_env: Option<&mut Environment>,
536) -> Maybe<TypeId> {
537 let syntax_db = db.upcast();
538 Ok(match ty_syntax {
539 ast::Expr::Path(path) => {
540 match resolver.resolve_concrete_path_ex(
541 diagnostics,
542 path,
543 NotFoundItemType::Type,
544 statement_env,
545 )? {
546 ResolvedConcreteItem::Type(ty) => ty,
547 _ => {
548 return Err(diagnostics.report(path, NotAType));
549 }
550 }
551 }
552 ast::Expr::Parenthesized(expr_syntax) => resolve_type_with_environment(
553 db,
554 diagnostics,
555 resolver,
556 &expr_syntax.expr(syntax_db),
557 statement_env,
558 ),
559 ast::Expr::Tuple(tuple_syntax) => {
560 let sub_tys = tuple_syntax
561 .expressions(syntax_db)
562 .elements(syntax_db)
563 .into_iter()
564 .map(|subexpr_syntax| {
565 resolve_type_with_environment(
566 db,
567 diagnostics,
568 resolver,
569 &subexpr_syntax,
570 statement_env.as_deref_mut(),
571 )
572 })
573 .collect();
574 TypeLongId::Tuple(sub_tys).intern(db)
575 }
576 ast::Expr::Unary(unary_syntax)
577 if matches!(unary_syntax.op(syntax_db), ast::UnaryOperator::At(_)) =>
578 {
579 let ty = resolve_type_with_environment(
580 db,
581 diagnostics,
582 resolver,
583 &unary_syntax.expr(syntax_db),
584 statement_env,
585 );
586 TypeLongId::Snapshot(ty).intern(db)
587 }
588 ast::Expr::Unary(unary_syntax)
589 if matches!(unary_syntax.op(syntax_db), ast::UnaryOperator::Desnap(_)) =>
590 {
591 let ty = resolve_type_with_environment(
592 db,
593 diagnostics,
594 resolver,
595 &unary_syntax.expr(syntax_db),
596 statement_env,
597 );
598 if let Some(desnapped_ty) =
599 try_extract_matches!(ty.lookup_intern(db), TypeLongId::Snapshot)
600 {
601 desnapped_ty
602 } else {
603 return Err(diagnostics.report(ty_syntax, DesnapNonSnapshot));
604 }
605 }
606 ast::Expr::FixedSizeArray(array_syntax) => {
607 let [ty] = &array_syntax.exprs(syntax_db).elements(syntax_db)[..] else {
608 return Err(diagnostics.report(ty_syntax, FixedSizeArrayTypeNonSingleType));
609 };
610 let ty = resolve_type_with_environment(db, diagnostics, resolver, ty, statement_env);
611 let size = match extract_fixed_size_array_size(db, diagnostics, array_syntax, resolver)?
612 {
613 Some(size) => size,
614 None => {
615 return Err(diagnostics.report(ty_syntax, FixedSizeArrayTypeEmptySize));
616 }
617 };
618 TypeLongId::FixedSizeArray { type_id: ty, size }.intern(db)
619 }
620 _ => {
621 return Err(diagnostics.report(ty_syntax, UnknownType));
622 }
623 })
624}
625
626pub fn extract_fixed_size_array_size(
629 db: &dyn SemanticGroup,
630 diagnostics: &mut SemanticDiagnostics,
631 syntax: &ast::ExprFixedSizeArray,
632 resolver: &Resolver<'_>,
633) -> Maybe<Option<ConstValueId>> {
634 let syntax_db = db.upcast();
635 match syntax.size(syntax_db) {
636 ast::OptionFixedSizeArraySize::FixedSizeArraySize(size_clause) => {
637 let environment = Environment::empty();
638 let resolver = Resolver::with_data(
639 db,
640 (resolver.data).clone_with_inference_id(db, resolver.inference_data.inference_id),
641 );
642 let mut ctx = ComputationContext::new(
643 db,
644 diagnostics,
645 resolver,
646 None,
647 environment,
648 ContextFunction::Global,
649 );
650 let size_expr_syntax = size_clause.size(syntax_db);
651 let size = compute_expr_semantic(&mut ctx, &size_expr_syntax);
652 let const_value = resolve_const_expr_and_evaluate(
653 db,
654 &mut ctx,
655 &size,
656 size_expr_syntax.stable_ptr().untyped(),
657 get_usize_ty(db),
658 );
659 if matches!(const_value, ConstValue::Int(_, _) | ConstValue::Generic(_)) {
660 Ok(Some(const_value.intern(db)))
661 } else {
662 Err(diagnostics.report(syntax, FixedSizeArrayNonNumericSize))
663 }
664 }
665 ast::OptionFixedSizeArraySize::Empty(_) => Ok(None),
666 }
667}
668
669pub fn verify_fixed_size_array_size(
671 diagnostics: &mut SemanticDiagnostics,
672 size: &BigInt,
673 syntax: &ast::ExprFixedSizeArray,
674) -> Maybe<()> {
675 if size > &BigInt::from(i16::MAX) {
676 return Err(diagnostics.report(syntax, FixedSizeArraySizeTooBig));
677 }
678 Ok(())
679}
680
681pub fn generic_type_generic_params(
683 db: &dyn SemanticGroup,
684 generic_type: GenericTypeId,
685) -> Maybe<Vec<semantic::GenericParam>> {
686 match generic_type {
687 GenericTypeId::Struct(id) => db.struct_generic_params(id),
688 GenericTypeId::Enum(id) => db.enum_generic_params(id),
689 GenericTypeId::Extern(id) => db.extern_type_declaration_generic_params(id),
690 }
691}
692
693#[derive(Clone, Debug, PartialEq, Eq)]
694pub struct TypeInfo {
695 pub droppable: Result<ImplId, InferenceError>,
696 pub copyable: Result<ImplId, InferenceError>,
697 pub destruct_impl: Result<ImplId, InferenceError>,
698 pub panic_destruct_impl: Result<ImplId, InferenceError>,
699}
700
701pub fn get_impl_at_context(
703 db: &dyn SemanticGroup,
704 lookup_context: ImplLookupContext,
705 concrete_trait_id: ConcreteTraitId,
706 stable_ptr: Option<SyntaxStablePtrId>,
707) -> Result<ImplId, InferenceError> {
708 let mut inference_data = InferenceData::new(InferenceId::NoContext);
709 let mut inference = inference_data.inference(db);
710 let constrains = db.generic_params_type_constraints(lookup_context.generic_params.clone());
711 inference.conform_generic_params_type_constraints(&constrains);
712 let impl_id = inference.new_impl_var(concrete_trait_id, stable_ptr, lookup_context);
715 if let Err((err_set, _)) = inference.finalize_without_reporting() {
716 return Err(inference
717 .consume_error_without_reporting(err_set)
718 .expect("Error couldn't be already consumed"));
719 };
720 Ok(inference.rewrite(impl_id).no_err())
721}
722
723pub fn single_value_type(db: &dyn SemanticGroup, ty: TypeId) -> Maybe<bool> {
725 Ok(match ty.lookup_intern(db) {
726 TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
727 ConcreteTypeId::Struct(id) => {
728 for member in db.struct_members(id.struct_id(db))?.values() {
729 if !db.single_value_type(member.ty)? {
730 return Ok(false);
731 }
732 }
733 true
734 }
735 ConcreteTypeId::Enum(id) => {
736 let variants = db.enum_variants(id.enum_id(db))?;
737 if variants.len() != 1 {
738 return Ok(false);
739 }
740
741 db.single_value_type(
742 db.variant_semantic(id.enum_id(db), *variants.values().next().unwrap())?.ty,
743 )?
744 }
745 ConcreteTypeId::Extern(_) => false,
746 },
747 TypeLongId::Tuple(types) => {
748 for ty in &types {
749 if !db.single_value_type(*ty)? {
750 return Ok(false);
751 }
752 }
753 true
754 }
755 TypeLongId::Snapshot(ty) => db.single_value_type(ty)?,
756 TypeLongId::GenericParameter(_)
757 | TypeLongId::Var(_)
758 | TypeLongId::Missing(_)
759 | TypeLongId::Coupon(_)
760 | TypeLongId::ImplType(_)
761 | TypeLongId::TraitType(_)
762 | TypeLongId::Closure(_) => false,
763 TypeLongId::FixedSizeArray { type_id, size } => {
764 db.single_value_type(type_id)?
765 || matches!(size.lookup_intern(db),
766 ConstValue::Int(value, _) if value.is_zero())
767 }
768 })
769}
770
771pub fn add_type_based_diagnostics(
773 db: &dyn SemanticGroup,
774 diagnostics: &mut SemanticDiagnostics,
775 ty: TypeId,
776 stable_ptr: impl Into<SyntaxStablePtrId> + Copy,
777) {
778 if db.type_size_info(ty) == Ok(TypeSizeInformation::Infinite) {
779 diagnostics.report(stable_ptr, InfiniteSizeType(ty));
780 }
781 if let TypeLongId::Concrete(ConcreteTypeId::Extern(extrn)) = ty.lookup_intern(db) {
782 let long_id = extrn.lookup_intern(db);
783 if long_id.extern_type_id.name(db.upcast()).as_str() == "Array" {
784 if let [GenericArgumentId::Type(arg_ty)] = &long_id.generic_args[..] {
785 if db.type_size_info(*arg_ty) == Ok(TypeSizeInformation::ZeroSized) {
786 diagnostics.report(stable_ptr, ArrayOfZeroSizedElements(*arg_ty));
787 }
788 }
789 }
790 }
791}
792
793#[derive(Clone, Debug, PartialEq, Eq)]
794pub enum TypeSizeInformation {
795 Infinite,
799 ZeroSized,
801 Other,
803}
804
805pub fn type_size_info(db: &dyn SemanticGroup, ty: TypeId) -> Maybe<TypeSizeInformation> {
807 match ty.lookup_intern(db) {
808 TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
809 ConcreteTypeId::Struct(id) => {
810 let mut zero_sized = true;
811 for (_, member) in db.struct_members(id.struct_id(db))?.iter() {
812 if db.type_size_info(member.ty)? != TypeSizeInformation::ZeroSized {
813 zero_sized = false;
814 }
815 }
816 if zero_sized {
817 return Ok(TypeSizeInformation::ZeroSized);
818 }
819 }
820 ConcreteTypeId::Enum(id) => {
821 for (_, variant) in db.enum_variants(id.enum_id(db))? {
822 db.type_size_info(db.variant_semantic(id.enum_id(db), variant)?.ty)?;
824 }
825 }
826 ConcreteTypeId::Extern(_) => {}
827 },
828 TypeLongId::Tuple(types) => {
829 let mut zero_sized = true;
830 for ty in types {
831 if db.type_size_info(ty)? != TypeSizeInformation::ZeroSized {
832 zero_sized = false;
833 }
834 }
835 if zero_sized {
836 return Ok(TypeSizeInformation::ZeroSized);
837 }
838 }
839 TypeLongId::Snapshot(ty) => {
840 if db.type_size_info(ty)? == TypeSizeInformation::ZeroSized {
841 return Ok(TypeSizeInformation::ZeroSized);
842 }
843 }
844 TypeLongId::Coupon(_) => return Ok(TypeSizeInformation::ZeroSized),
845 TypeLongId::GenericParameter(_)
846 | TypeLongId::Var(_)
847 | TypeLongId::Missing(_)
848 | TypeLongId::TraitType(_)
849 | TypeLongId::ImplType(_)
850 | TypeLongId::Closure(_) => {}
851 TypeLongId::FixedSizeArray { type_id, size } => {
852 if matches!(size.lookup_intern(db), ConstValue::Int(value,_) if value.is_zero())
853 || db.type_size_info(type_id)? == TypeSizeInformation::ZeroSized
854 {
855 return Ok(TypeSizeInformation::ZeroSized);
856 }
857 }
858 }
859 Ok(TypeSizeInformation::Other)
860}
861
862pub fn type_size_info_cycle(
864 _db: &dyn SemanticGroup,
865 _cycle: &salsa::Cycle,
866 _ty: &TypeId,
867) -> Maybe<TypeSizeInformation> {
868 Ok(TypeSizeInformation::Infinite)
869}
870
871pub fn type_info(
875 db: &dyn SemanticGroup,
876 lookup_context: ImplLookupContext,
877 ty: TypeId,
878) -> Maybe<TypeInfo> {
879 let droppable =
881 get_impl_at_context(db, lookup_context.clone(), concrete_drop_trait(db, ty), None);
882 let copyable =
883 get_impl_at_context(db, lookup_context.clone(), concrete_copy_trait(db, ty), None);
884 let destruct_impl =
885 get_impl_at_context(db, lookup_context.clone(), concrete_destruct_trait(db, ty), None);
886 let panic_destruct_impl =
887 get_impl_at_context(db, lookup_context, concrete_panic_destruct_trait(db, ty), None);
888 Ok(TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl })
889}
890
891pub fn priv_type_is_fully_concrete(db: &dyn SemanticGroup, ty: TypeId) -> bool {
892 match ty.lookup_intern(db) {
893 TypeLongId::Concrete(concrete_type_id) => concrete_type_id.is_fully_concrete(db),
894 TypeLongId::Tuple(types) => types.iter().all(|ty| ty.is_fully_concrete(db)),
895 TypeLongId::Snapshot(ty) => ty.is_fully_concrete(db),
896 TypeLongId::GenericParameter(_)
897 | TypeLongId::Var(_)
898 | TypeLongId::Missing(_)
899 | TypeLongId::ImplType(_)
900 | TypeLongId::TraitType(_) => false,
901 TypeLongId::Coupon(function_id) => function_id.is_fully_concrete(db),
902 TypeLongId::FixedSizeArray { type_id, size } => {
903 type_id.is_fully_concrete(db) && size.is_fully_concrete(db)
904 }
905 TypeLongId::Closure(closure) => {
906 closure.param_tys.iter().all(|param| param.is_fully_concrete(db))
907 && closure.ret_ty.is_fully_concrete(db)
908 }
909 }
910}
911
912pub fn priv_type_is_var_free(db: &dyn SemanticGroup, ty: TypeId) -> bool {
913 match ty.lookup_intern(db) {
914 TypeLongId::Concrete(concrete_type_id) => concrete_type_id.is_var_free(db),
915 TypeLongId::Tuple(types) => types.iter().all(|ty| ty.is_var_free(db)),
916 TypeLongId::Snapshot(ty) => ty.is_var_free(db),
917 TypeLongId::Var(_) => false,
918 TypeLongId::GenericParameter(_) | TypeLongId::Missing(_) | TypeLongId::TraitType(_) => true,
919 TypeLongId::Coupon(function_id) => function_id.is_var_free(db),
920 TypeLongId::FixedSizeArray { type_id, size } => {
921 type_id.is_var_free(db) && size.is_var_free(db)
922 }
923 TypeLongId::ImplType(_) => false,
926 TypeLongId::Closure(closure) => {
927 closure.param_tys.iter().all(|param| param.is_var_free(db))
928 && closure.ret_ty.is_var_free(db)
929 }
930 }
931}
932
933pub fn priv_type_short_name(db: &dyn SemanticGroup, ty: TypeId) -> String {
934 match ty.lookup_intern(db) {
935 TypeLongId::Concrete(concrete_type_id) => {
936 let mut result = concrete_type_id.generic_type(db).format(db.upcast());
937 let mut generic_args = concrete_type_id.generic_args(db).into_iter().peekable();
938 if generic_args.peek().is_some() {
939 result.push_str("::<h0x");
940 let mut hasher = Keccak256::new();
941 for arg in generic_args {
942 hasher.update(arg.short_name(db).as_bytes());
943 }
944 for c in hasher.finalize() {
945 result.push_str(&format!("{c:x}"));
946 }
947 result.push('>');
948 }
949 result
950 }
951 TypeLongId::Tuple(types) => {
952 let mut result = String::from("(h0x");
953 let mut hasher = Keccak256::new();
954 for ty in types {
955 hasher.update(ty.short_name(db).as_bytes());
956 }
957 for c in hasher.finalize() {
958 result.push_str(&format!("{c:x}"));
959 }
960 result.push(')');
961 result
962 }
963 TypeLongId::Snapshot(ty) => {
964 format!("@{}", ty.short_name(db))
965 }
966 TypeLongId::FixedSizeArray { type_id, size } => {
967 format!("[{}; {:?}", type_id.short_name(db), size.debug(db.elongate()))
968 }
969 other => other.format(db),
970 }
971}
972
973pub fn peel_snapshots(db: &dyn SemanticGroup, ty: TypeId) -> (usize, TypeLongId) {
976 peel_snapshots_ex(db, ty.lookup_intern(db))
977}
978
979pub fn peel_snapshots_ex(db: &dyn SemanticGroup, mut long_ty: TypeLongId) -> (usize, TypeLongId) {
981 let mut n_snapshots = 0;
982 while let TypeLongId::Snapshot(ty) = long_ty {
983 long_ty = ty.lookup_intern(db);
984 n_snapshots += 1;
985 }
986 (n_snapshots, long_ty)
987}
988
989pub fn wrap_in_snapshots(db: &dyn SemanticGroup, mut ty: TypeId, n_snapshots: usize) -> TypeId {
991 for _ in 0..n_snapshots {
992 ty = TypeLongId::Snapshot(ty).intern(db);
993 }
994 ty
995}
996
997pub(crate) fn are_coupons_enabled(db: &dyn SemanticGroup, module_file_id: ModuleFileId) -> bool {
999 let owning_crate = module_file_id.0.owning_crate(db.upcast());
1000 let Some(config) = db.crate_config(owning_crate) else { return false };
1001 config.settings.experimental_features.coupons
1002}