1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_defs::db::DefsGroup;
3use cairo_lang_defs::diagnostic_utils::StableLocation;
4use cairo_lang_defs::ids::{
5 EnumId, ExternTypeId, GenericParamId, GenericTypeId, LanguageElementId, ModuleId,
6 NamedLanguageElementId, StructId, TraitTypeId, UnstableSalsaId,
7};
8use cairo_lang_diagnostics::{DiagnosticAdded, Maybe};
9use cairo_lang_filesystem::db::FilesGroup;
10use cairo_lang_proc_macros::{HeapSize, SemanticObject};
11use cairo_lang_syntax::attribute::consts::MUST_USE_ATTR;
12use cairo_lang_syntax::node::ast::PathSegment;
13use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
14use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast};
15use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
16use cairo_lang_utils::{Intern, OptionFrom, define_short_id, extract_matches, try_extract_matches};
17use itertools::{Itertools, chain};
18use num_bigint::BigInt;
19use num_traits::Zero;
20use salsa::Database;
21use sha3::{Digest, Keccak256};
22
23use crate::corelib::{
24 CorelibSemantic, concrete_copy_trait, concrete_destruct_trait, concrete_drop_trait,
25 concrete_panic_destruct_trait, core_box_ty, get_usize_ty, unit_ty,
26};
27use crate::diagnostic::SemanticDiagnosticKind::*;
28use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder};
29use crate::expr::compute::{ComputationContext, compute_expr_semantic};
30use crate::expr::inference::canonic::{CanonicalTrait, ResultNoErrEx};
31use crate::expr::inference::solver::{SemanticSolver, SolutionSet, enrich_lookup_context};
32use crate::expr::inference::{InferenceData, InferenceError, InferenceId, TypeVar};
33use crate::items::attribute::SemanticQueryAttrs;
34use crate::items::constant::{ConstValue, ConstValueId, resolve_const_expr_and_evaluate};
35use crate::items::enm::{EnumSemantic, SemanticEnumEx};
36use crate::items::extern_type::ExternTypeSemantic;
37use crate::items::generics::{GenericParamSemantic, displayable_concrete};
38use crate::items::imp::{ImplId, ImplLookupContext, ImplLookupContextId, ImplSemantic};
39use crate::items::structure::StructSemantic;
40use crate::resolve::{ResolutionContext, ResolvedConcreteItem, ResolvedGenericItem, Resolver};
41use crate::substitution::SemanticRewriter;
42use crate::{ConcreteTraitId, FunctionId, GenericArgumentId, semantic, semantic_object_for_id};
43
44#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
45pub enum TypeLongId<'db> {
46 Concrete(ConcreteTypeId<'db>),
47 Tuple(Vec<TypeId<'db>>),
50 Snapshot(TypeId<'db>),
51 GenericParameter(GenericParamId<'db>),
52 Var(TypeVar<'db>),
53 NumericLiteral(TypeVar<'db>),
59 Coupon(FunctionId<'db>),
60 FixedSizeArray {
61 type_id: TypeId<'db>,
62 size: ConstValueId<'db>,
63 },
64 ImplType(ImplTypeId<'db>),
65 Closure(ClosureTypeLongId<'db>),
66 Missing(#[dont_rewrite] DiagnosticAdded),
67}
68impl<'db> OptionFrom<TypeLongId<'db>> for ConcreteTypeId<'db> {
69 fn option_from(other: TypeLongId<'db>) -> Option<Self> {
70 try_extract_matches!(other, TypeLongId::Concrete)
71 }
72}
73
74define_short_id!(TypeId, TypeLongId<'db>);
75semantic_object_for_id!(TypeId, TypeLongId<'a>);
76impl<'db> TypeId<'db> {
77 pub fn missing(db: &'db dyn Database, diag_added: DiagnosticAdded) -> Self {
78 TypeLongId::Missing(diag_added).intern(db)
79 }
80
81 pub fn format(&self, db: &dyn Database) -> String {
82 self.long(db).format(db)
83 }
84
85 pub fn check_not_missing(&self, db: &dyn Database) -> Maybe<()> {
87 if let TypeLongId::Missing(diag_added) = self.long(db) { Err(*diag_added) } else { Ok(()) }
88 }
89
90 pub fn is_missing(&self, db: &dyn Database) -> bool {
92 self.check_not_missing(db).is_err()
93 }
94
95 pub fn is_unit(&self, db: &dyn Database) -> bool {
97 matches!(self.long(db), TypeLongId::Tuple(types) if types.is_empty())
98 }
99
100 pub fn head(&self, db: &'db dyn Database) -> Option<TypeHead<'db>> {
102 self.long(db).head(db)
103 }
104
105 pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
107 db.priv_type_is_fully_concrete(*self)
108 }
109
110 pub fn is_var_free(&self, db: &dyn Database) -> bool {
112 db.priv_type_is_var_free(*self)
113 }
114
115 pub fn is_phantom(&self, db: &dyn Database) -> bool {
119 self.long(db).is_phantom(db)
120 }
121
122 pub fn short_name(&self, db: &dyn Database) -> String {
124 db.priv_type_short_name(*self)
125 }
126}
127impl<'db> TypeLongId<'db> {
128 pub fn format(&self, db: &dyn Database) -> String {
129 format!("{:?}", self.debug(db))
130 }
131
132 pub fn head(&self, db: &'db dyn Database) -> Option<TypeHead<'db>> {
134 Some(match self {
135 TypeLongId::Concrete(concrete) => TypeHead::Concrete(concrete.generic_type(db)),
136 TypeLongId::Tuple(_) => TypeHead::Tuple,
137 TypeLongId::Snapshot(inner) => TypeHead::Snapshot(Box::new(inner.head(db)?)),
138 TypeLongId::Coupon(_) => TypeHead::Coupon,
139 TypeLongId::FixedSizeArray { .. } => TypeHead::FixedSizeArray,
140 TypeLongId::GenericParameter(generic_param_id) => TypeHead::Generic(*generic_param_id),
141 TypeLongId::Var(_)
142 | TypeLongId::NumericLiteral(_)
143 | TypeLongId::Missing(_)
144 | TypeLongId::ImplType(_)
145 | TypeLongId::Closure(_) => {
146 return None;
147 }
148 })
149 }
150
151 pub fn is_phantom(&self, db: &dyn Database) -> bool {
156 match self {
157 TypeLongId::Concrete(id) => match id {
158 ConcreteTypeId::Struct(id) => {
159 let crate_id = id.struct_id(db).long(db).0.owning_crate(db);
160
161 db.declared_phantom_type_attributes(crate_id)
162 .iter()
163 .any(|attr| id.has_attr(db, attr.long(db)).unwrap_or_default())
164 }
165 ConcreteTypeId::Enum(id) => {
166 let crate_id = id.enum_id(db).long(db).0.owning_crate(db);
167
168 db.declared_phantom_type_attributes(crate_id)
169 .iter()
170 .any(|attr| id.has_attr(db, attr.long(db)).unwrap_or_default())
171 }
172 ConcreteTypeId::Extern(id) => {
173 let crate_id = id.extern_type_id(db).long(db).0.owning_crate(db);
174
175 db.declared_phantom_type_attributes(crate_id)
176 .iter()
177 .any(|attr| id.has_attr(db, attr.long(db)).unwrap_or_default())
178 }
179 },
180 TypeLongId::Tuple(inner) => inner.iter().any(|ty| ty.is_phantom(db)),
181 TypeLongId::FixedSizeArray { type_id, .. } => type_id.is_phantom(db),
182 TypeLongId::Snapshot(_)
183 | TypeLongId::GenericParameter(_)
184 | TypeLongId::Var(_)
185 | TypeLongId::NumericLiteral(_)
186 | TypeLongId::Coupon(_)
187 | TypeLongId::ImplType(_)
188 | TypeLongId::Missing(_)
189 | TypeLongId::Closure(_) => false,
190 }
191 }
192
193 pub fn module_id(&self, db: &'db dyn Database) -> Option<ModuleId<'db>> {
195 match self {
196 TypeLongId::Concrete(concrete) => Some(concrete.generic_type(db).parent_module(db)),
197 TypeLongId::Snapshot(ty) => {
198 let (_n_snapshots, inner_ty) = peel_snapshots(db, *ty);
199 inner_ty.module_id(db)
200 }
201 TypeLongId::GenericParameter(_) => None,
202 TypeLongId::Var(_) => None,
203 TypeLongId::NumericLiteral(_) => None,
204 TypeLongId::Coupon(function_id) => {
205 function_id.get_concrete(db).generic_function.module_id(db)
206 }
207 TypeLongId::Missing(_) => None,
208 TypeLongId::Tuple(_) => Some(db.core_info().tuple_submodule),
209 TypeLongId::ImplType(_) => None,
210 TypeLongId::FixedSizeArray { .. } => Some(db.core_info().fixed_size_array_submodule),
211 TypeLongId::Closure(closure) => {
212 if let Ok(function_id) = closure.parent_function {
213 function_id.get_concrete(db).generic_function.module_id(db)
214 } else {
215 None
216 }
217 }
218 }
219 }
220
221 pub fn extract_generic_params(
224 &self,
225 db: &'db dyn Database,
226 generic_parameters: &mut OrderedHashSet<GenericParamId<'db>>,
227 visited: &mut OrderedHashSet<TypeId<'db>>,
228 ) -> Maybe<()> {
229 match self {
230 TypeLongId::Concrete(concrete_type_id) => {
231 for garg in concrete_type_id.generic_args(db) {
232 garg.extract_generic_params(db, generic_parameters, visited)?;
233 }
234 }
235 TypeLongId::Tuple(tys) => {
236 for ty in tys {
237 ty.extract_generic_params(db, generic_parameters, visited)?
238 }
239 }
240 TypeLongId::Snapshot(ty) => {
241 ty.extract_generic_params(db, generic_parameters, visited)?
242 }
243 TypeLongId::GenericParameter(generic_param) => {
244 generic_parameters.insert(*generic_param);
245 }
246 TypeLongId::Var(_) => {}
247 TypeLongId::NumericLiteral(_) => {}
248 TypeLongId::Coupon(_) => {}
249 TypeLongId::FixedSizeArray { type_id, size } => {
250 type_id.extract_generic_params(db, generic_parameters, visited)?;
251 size.extract_generic_params(db, generic_parameters, visited)?;
252 }
253 TypeLongId::ImplType(impl_type_id) => {
254 let concrete_trait_id = impl_type_id.impl_id.concrete_trait(db)?;
255 for garg in concrete_trait_id.generic_args(db) {
256 garg.extract_generic_params(db, generic_parameters, visited)?;
257 }
258 }
259 TypeLongId::Closure(closure_ty) => {
260 for ty in chain!(
261 &closure_ty.param_tys,
262 &closure_ty.captured_types,
263 std::iter::once(&closure_ty.ret_ty)
264 ) {
265 ty.extract_generic_params(db, generic_parameters, visited)?
266 }
267 }
268 TypeLongId::Missing(diag_added) => return Err(*diag_added),
269 };
270 Ok(())
271 }
272}
273
274impl<'db> TypeId<'db> {
275 pub fn extract_generic_params(
278 self,
279 db: &'db dyn Database,
280 generic_parameters: &mut OrderedHashSet<GenericParamId<'db>>,
281 visited: &mut OrderedHashSet<TypeId<'db>>,
282 ) -> Maybe<()> {
283 if !visited.insert(self) {
284 return Ok(());
285 }
286 self.long(db).extract_generic_params(db, generic_parameters, visited)
287 }
288}
289impl<'db> DebugWithDb<'db> for TypeLongId<'db> {
290 type Db = dyn Database;
291
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
293 match self {
294 TypeLongId::Concrete(concrete) => write!(f, "{}", concrete.format(db)),
295 TypeLongId::Tuple(inner_types) => {
296 if inner_types.len() == 1 {
297 write!(f, "({},)", inner_types[0].format(db))
298 } else {
299 write!(f, "({})", inner_types.iter().map(|ty| ty.format(db)).join(", "))
300 }
301 }
302 TypeLongId::Snapshot(ty) => write!(f, "@{}", ty.format(db)),
303 TypeLongId::GenericParameter(generic_param) => {
304 write!(f, "{}", generic_param.name(db).map_or("_", |name| name.long(db)))
305 }
306 TypeLongId::ImplType(impl_type_id) => {
307 write!(
308 f,
309 "{:?}::{}",
310 impl_type_id.impl_id.debug(db),
311 impl_type_id.ty.name(db).long(db)
312 )
313 }
314 TypeLongId::Var(var) => write!(f, "?{}", var.id.0),
315 TypeLongId::NumericLiteral(_) => write!(f, "{{numeric}}"),
316 TypeLongId::Coupon(function_id) => write!(f, "{}::Coupon", function_id.full_path(db)),
317 TypeLongId::Missing(_) => write!(f, "<missing>"),
318 TypeLongId::FixedSizeArray { type_id, size } => {
319 write!(f, "[{}; {:?}]", type_id.format(db), size.debug(db))
320 }
321 TypeLongId::Closure(closure) => {
322 write!(f, "{:?}", closure.debug(db))
323 }
324 }
325 }
326}
327
328#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
334pub enum TypeHead<'db> {
335 Concrete(GenericTypeId<'db>),
336 Snapshot(Box<TypeHead<'db>>),
337 Generic(GenericParamId<'db>),
338 Tuple,
339 Coupon,
340 FixedSizeArray,
341}
342
343#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
344pub enum ConcreteTypeId<'db> {
345 Struct(ConcreteStructId<'db>),
346 Enum(ConcreteEnumId<'db>),
347 Extern(ConcreteExternTypeId<'db>),
348}
349impl<'db> ConcreteTypeId<'db> {
350 pub fn new(
351 db: &'db dyn Database,
352 generic_ty: GenericTypeId<'db>,
353 generic_args: Vec<semantic::GenericArgumentId<'db>>,
354 ) -> Self {
355 match generic_ty {
356 GenericTypeId::Struct(id) => ConcreteTypeId::Struct(
357 ConcreteStructLongId { struct_id: id, generic_args }.intern(db),
358 ),
359 GenericTypeId::Enum(id) => {
360 ConcreteTypeId::Enum(ConcreteEnumLongId { enum_id: id, generic_args }.intern(db))
361 }
362 GenericTypeId::Extern(id) => ConcreteTypeId::Extern(
363 ConcreteExternTypeLongId { extern_type_id: id, generic_args }.intern(db),
364 ),
365 }
366 }
367 pub fn generic_type(&self, db: &'db dyn Database) -> GenericTypeId<'db> {
368 match self {
369 ConcreteTypeId::Struct(id) => GenericTypeId::Struct(id.long(db).struct_id),
370 ConcreteTypeId::Enum(id) => GenericTypeId::Enum(id.long(db).enum_id),
371 ConcreteTypeId::Extern(id) => GenericTypeId::Extern(id.long(db).extern_type_id),
372 }
373 }
374 pub fn generic_args(&self, db: &'db dyn Database) -> Vec<semantic::GenericArgumentId<'db>> {
375 match self {
376 ConcreteTypeId::Struct(id) => id.long(db).generic_args.clone(),
377 ConcreteTypeId::Enum(id) => id.long(db).generic_args.clone(),
378 ConcreteTypeId::Extern(id) => id.long(db).generic_args.clone(),
379 }
380 }
381 pub fn format(&self, db: &dyn Database) -> String {
382 format!("{:?}", self.debug(db))
383 }
384
385 pub fn is_must_use(&self, db: &dyn Database) -> Maybe<bool> {
387 match self {
388 ConcreteTypeId::Struct(id) => id.has_attr(db, MUST_USE_ATTR),
389 ConcreteTypeId::Enum(id) => id.has_attr(db, MUST_USE_ATTR),
390 ConcreteTypeId::Extern(id) => id.has_attr(db, MUST_USE_ATTR),
391 }
392 }
393 pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
395 self.generic_args(db)
396 .iter()
397 .all(|generic_argument_id| generic_argument_id.is_fully_concrete(db))
398 }
399 pub fn is_var_free(&self, db: &dyn Database) -> bool {
401 self.generic_args(db).iter().all(|generic_argument_id| generic_argument_id.is_var_free(db))
402 }
403}
404impl<'db> DebugWithDb<'db> for ConcreteTypeId<'db> {
405 type Db = dyn Database;
406
407 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
408 write!(
409 f,
410 "{}",
411 displayable_concrete(db, &self.generic_type(db).format(db), &self.generic_args(db))
412 )
413 }
414}
415
416#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
417pub struct ConcreteStructLongId<'db> {
418 pub struct_id: StructId<'db>,
419 pub generic_args: Vec<semantic::GenericArgumentId<'db>>,
420}
421define_short_id!(ConcreteStructId, ConcreteStructLongId<'db>);
422semantic_object_for_id!(ConcreteStructId, ConcreteStructLongId<'a>);
423impl<'db> ConcreteStructId<'db> {
424 pub fn struct_id(&self, db: &'db dyn Database) -> StructId<'db> {
425 self.long(db).struct_id
426 }
427}
428impl<'db> DebugWithDb<'db> for ConcreteStructLongId<'db> {
429 type Db = dyn Database;
430
431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
432 write!(f, "{:?}", ConcreteTypeId::Struct(self.clone().intern(db)).debug(db))
433 }
434}
435
436#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
437pub struct ConcreteEnumLongId<'db> {
438 pub enum_id: EnumId<'db>,
439 pub generic_args: Vec<semantic::GenericArgumentId<'db>>,
440}
441impl<'db> DebugWithDb<'db> for ConcreteEnumLongId<'db> {
442 type Db = dyn Database;
443
444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
445 write!(f, "{:?}", ConcreteTypeId::Enum(self.clone().intern(db)).debug(db))
446 }
447}
448
449define_short_id!(ConcreteEnumId, ConcreteEnumLongId<'db>);
450semantic_object_for_id!(ConcreteEnumId, ConcreteEnumLongId<'a>);
451impl<'db> ConcreteEnumId<'db> {
452 pub fn enum_id(&self, db: &'db dyn Database) -> EnumId<'db> {
453 self.long(db).enum_id
454 }
455}
456
457#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
458pub struct ConcreteExternTypeLongId<'db> {
459 pub extern_type_id: ExternTypeId<'db>,
460 pub generic_args: Vec<semantic::GenericArgumentId<'db>>,
461}
462define_short_id!(ConcreteExternTypeId, ConcreteExternTypeLongId<'db>);
463semantic_object_for_id!(ConcreteExternTypeId, ConcreteExternTypeLongId<'a>);
464impl<'db> ConcreteExternTypeId<'db> {
465 pub fn extern_type_id(&self, db: &'db dyn Database) -> ExternTypeId<'db> {
466 self.long(db).extern_type_id
467 }
468}
469
470#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, salsa::Update, HeapSize)]
472pub struct ClosureTypeLongId<'db> {
473 pub param_tys: Vec<TypeId<'db>>,
474 pub ret_ty: TypeId<'db>,
475 pub captured_types: Vec<TypeId<'db>>,
479 pub parent_function: Maybe<FunctionId<'db>>,
481 #[dont_rewrite]
483 pub params_location: StableLocation<'db>,
484}
485
486impl<'db> DebugWithDb<'db> for ClosureTypeLongId<'db> {
487 type Db = dyn Database;
488
489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
490 write!(f, "{{closure@{:?}}}", self.params_location.debug(db))
491 }
492}
493
494#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject, HeapSize, salsa::Update)]
496pub struct ImplTypeId<'db> {
497 impl_id: ImplId<'db>,
499 ty: TraitTypeId<'db>,
501}
502impl<'db> ImplTypeId<'db> {
503 pub fn new(impl_id: ImplId<'db>, ty: TraitTypeId<'db>, db: &'db dyn Database) -> Self {
506 if let crate::items::imp::ImplLongId::Concrete(concrete_impl) = impl_id.long(db) {
507 let impl_def_id = concrete_impl.impl_def_id(db);
508 assert_eq!(Ok(ty.trait_id(db)), db.impl_def_trait(impl_def_id));
509 }
510
511 ImplTypeId { impl_id, ty }
512 }
513 pub fn impl_id(&self) -> ImplId<'db> {
514 self.impl_id
515 }
516 pub fn ty(&self) -> TraitTypeId<'db> {
517 self.ty
518 }
519 pub fn format(&self, db: &dyn Database) -> String {
520 format!("{}::{}", self.impl_id.name(db), self.ty.name(db).long(db))
521 }
522}
523impl<'db> DebugWithDb<'db> for ImplTypeId<'db> {
524 type Db = dyn Database;
525
526 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
527 write!(f, "{}", self.format(db))
528 }
529}
530
531#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
533pub struct ImplTypeById<'db>(ImplTypeId<'db>);
534
535impl<'db> From<ImplTypeId<'db>> for ImplTypeById<'db> {
536 fn from(impl_type_id: ImplTypeId<'db>) -> Self {
537 Self(impl_type_id)
538 }
539}
540impl<'db> Ord for ImplTypeById<'db> {
541 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
542 self.0
543 .impl_id
544 .get_internal_id()
545 .cmp(&other.0.impl_id.get_internal_id())
546 .then_with(|| self.0.ty.get_internal_id().cmp(&other.0.ty.get_internal_id()))
547 }
548}
549impl<'db> PartialOrd for ImplTypeById<'db> {
550 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
551 Some(self.cmp(other))
552 }
553}
554
555pub fn resolve_type<'db>(
558 db: &'db dyn Database,
559 diagnostics: &mut SemanticDiagnostics<'db>,
560 resolver: &mut Resolver<'db>,
561 ty_syntax: &ast::Expr<'db>,
562) -> TypeId<'db> {
563 resolve_type_ex(db, diagnostics, resolver, ty_syntax, ResolutionContext::Default)
564}
565pub fn resolve_type_ex<'db>(
567 db: &'db dyn Database,
568 diagnostics: &mut SemanticDiagnostics<'db>,
569 resolver: &mut Resolver<'db>,
570 ty_syntax: &ast::Expr<'db>,
571 ctx: ResolutionContext<'db, '_>,
572) -> TypeId<'db> {
573 maybe_resolve_type(db, diagnostics, resolver, ty_syntax, ctx)
574 .unwrap_or_else(|diag_added| TypeId::missing(db, diag_added))
575}
576fn maybe_resolve_type<'db>(
577 db: &'db dyn Database,
578 diagnostics: &mut SemanticDiagnostics<'db>,
579 resolver: &mut Resolver<'db>,
580 ty_syntax: &ast::Expr<'db>,
581 mut ctx: ResolutionContext<'db, '_>,
582) -> Maybe<TypeId<'db>> {
583 Ok(match ty_syntax {
584 ast::Expr::Underscore(underscore) => {
585 resolver.inference().new_type_var(Some(underscore.stable_ptr(db).untyped()))
586 }
587 ast::Expr::Path(path) => {
588 match resolver.resolve_concrete_path_ex(
589 diagnostics,
590 path,
591 NotFoundItemType::Type,
592 ctx,
593 )? {
594 ResolvedConcreteItem::Type(ty) => ty,
595 _ => {
596 return Err(diagnostics.report(path.stable_ptr(db), NotAType));
597 }
598 }
599 }
600 ast::Expr::Parenthesized(expr_syntax) => {
601 resolve_type_ex(db, diagnostics, resolver, &expr_syntax.expr(db), ctx)
602 }
603 ast::Expr::Tuple(tuple_syntax) => {
604 let sub_tys = tuple_syntax
605 .expressions(db)
606 .elements(db)
607 .map(|subexpr_syntax| {
608 resolve_type_ex(
609 db,
610 diagnostics,
611 resolver,
612 &subexpr_syntax,
613 match ctx {
614 ResolutionContext::Default => ResolutionContext::Default,
615 ResolutionContext::ModuleItem(id) => ResolutionContext::ModuleItem(id),
616 ResolutionContext::Statement(ref mut env) => {
617 ResolutionContext::Statement(env)
618 }
619 },
620 )
621 })
622 .collect();
623 TypeLongId::Tuple(sub_tys).intern(db)
624 }
625 ast::Expr::Unary(unary_syntax)
626 if matches!(unary_syntax.op(db), ast::UnaryOperator::At(_)) =>
627 {
628 let ty = resolve_type_ex(db, diagnostics, resolver, &unary_syntax.expr(db), ctx);
629 TypeLongId::Snapshot(ty).intern(db)
630 }
631 ast::Expr::Unary(unary_syntax)
633 if matches!(unary_syntax.op(db), ast::UnaryOperator::Desnap(_)) =>
634 {
635 let ty = resolve_type_ex(db, diagnostics, resolver, &unary_syntax.expr(db), ctx);
636 if let Some(desnapped_ty) = try_extract_matches!(ty.long(db), TypeLongId::Snapshot) {
637 *desnapped_ty
638 } else {
639 return Err(diagnostics.report(ty_syntax.stable_ptr(db), DerefNonRef { ty }));
640 }
641 }
642 ast::Expr::Unary(unary_syntax)
643 if matches!(unary_syntax.op(db), ast::UnaryOperator::Reference(_)) =>
644 {
645 if !are_repr_ptrs_enabled(db, resolver.module_id) {
646 return Err(diagnostics.report(ty_syntax.stable_ptr(db), ReprPtrsDisabled));
647 }
648 let inner_ty = resolve_type_ex(db, diagnostics, resolver, &unary_syntax.expr(db), ctx);
649 let snapshot_ty = TypeLongId::Snapshot(inner_ty).intern(db);
650 core_box_ty(db, snapshot_ty)
651 }
652 ast::Expr::FixedSizeArray(array_syntax) => {
653 let Ok(ty) = &array_syntax.exprs(db).elements(db).exactly_one() else {
654 return Err(
655 diagnostics.report(ty_syntax.stable_ptr(db), FixedSizeArrayTypeNonSingleType)
656 );
657 };
658 let ty = resolve_type_ex(db, diagnostics, resolver, ty, ctx);
659 let size =
660 match extract_fixed_size_array_size(db, diagnostics, array_syntax, resolver)? {
661 Some(size) => size,
662 None => {
663 return Err(diagnostics
664 .report(ty_syntax.stable_ptr(db), FixedSizeArrayTypeEmptySize));
665 }
666 };
667 TypeLongId::FixedSizeArray { type_id: ty, size }.intern(db)
668 }
669 _ => {
670 return Err(diagnostics.report(ty_syntax.stable_ptr(db), UnknownType));
671 }
672 })
673}
674
675#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
677pub enum ShallowGenericArg<'db> {
678 GenericParameter(GenericParamId<'db>),
680 GenericType(GenericTypeId<'db>),
682 Snapshot(Box<ShallowGenericArg<'db>>),
683 Tuple,
684 FixedSizeArray,
685}
686
687impl<'db> ShallowGenericArg<'db> {
688 pub fn module_id(&self, db: &'db dyn Database) -> Option<ModuleId<'db>> {
690 match self {
691 ShallowGenericArg::GenericParameter(_) => None,
692 ShallowGenericArg::GenericType(ty) => Some(ty.parent_module(db)),
693 ShallowGenericArg::Snapshot(inner) => inner.module_id(db),
694 ShallowGenericArg::Tuple => TypeLongId::Tuple(vec![]).module_id(db),
695 ShallowGenericArg::FixedSizeArray => TypeLongId::FixedSizeArray {
696 type_id: unit_ty(db),
697 size: ConstValue::Struct(vec![], unit_ty(db)).intern(db),
698 }
699 .module_id(db),
700 }
701 }
702 pub fn head(&self) -> TypeHead<'db> {
703 match self {
704 ShallowGenericArg::GenericParameter(param) => TypeHead::Generic(*param),
705 ShallowGenericArg::GenericType(ty) => TypeHead::Concrete(*ty),
706 ShallowGenericArg::Snapshot(inner) => TypeHead::Snapshot(Box::new(inner.head())),
707 ShallowGenericArg::Tuple => TypeHead::Tuple,
708 ShallowGenericArg::FixedSizeArray => TypeHead::FixedSizeArray,
709 }
710 }
711}
712pub fn maybe_resolve_shallow_generic_arg_type<'db>(
714 db: &'db dyn Database,
715 diagnostics: &mut SemanticDiagnostics<'db>,
716 resolver: &mut Resolver<'db>,
717 ty_syntax: &ast::Expr<'db>,
718) -> Option<ShallowGenericArg<'db>> {
719 Some(match ty_syntax {
720 ast::Expr::Path(path) => {
721 if let [PathSegment::Simple(path)] =
722 path.segments(db).elements(db).collect_vec().as_slice()
723 && let Some(ResolvedConcreteItem::Type(ty)) =
724 resolver.determine_base_item_in_local_scope(&path.ident(db))
725 {
726 let param = extract_matches!(ty.long(db), TypeLongId::GenericParameter);
727 return Some(ShallowGenericArg::GenericParameter(*param));
728 }
729
730 match resolver
731 .resolve_generic_path_with_args(
732 diagnostics,
733 path,
734 NotFoundItemType::Type,
735 ResolutionContext::Default,
736 )
737 .ok()?
738 {
739 ResolvedGenericItem::GenericType(ty) => ShallowGenericArg::GenericType(ty),
740 _ => {
741 return None;
742 }
743 }
744 }
745 ast::Expr::Parenthesized(expr_syntax) => maybe_resolve_shallow_generic_arg_type(
746 db,
747 diagnostics,
748 resolver,
749 &expr_syntax.expr(db),
750 )?,
751 ast::Expr::Tuple(_) => ShallowGenericArg::Tuple,
752 ast::Expr::Unary(unary_syntax)
753 if matches!(unary_syntax.op(db), ast::UnaryOperator::At(_)) =>
754 {
755 ShallowGenericArg::Snapshot(Box::new(maybe_resolve_shallow_generic_arg_type(
756 db,
757 diagnostics,
758 resolver,
759 &unary_syntax.expr(db),
760 )?))
761 }
762 ast::Expr::Unary(unary_syntax)
763 if matches!(unary_syntax.op(db), ast::UnaryOperator::Desnap(_)) =>
764 {
765 maybe_resolve_shallow_generic_arg_type(
766 db,
767 diagnostics,
768 resolver,
769 &unary_syntax.expr(db),
770 )?
771 }
772 ast::Expr::FixedSizeArray(_) => ShallowGenericArg::FixedSizeArray,
773 _ => {
774 return None;
775 }
776 })
777}
778
779pub fn extract_fixed_size_array_size<'db>(
782 db: &'db dyn Database,
783 diagnostics: &mut SemanticDiagnostics<'db>,
784 syntax: &ast::ExprFixedSizeArray<'db>,
785 resolver: &mut Resolver<'db>,
786) -> Maybe<Option<ConstValueId<'db>>> {
787 match syntax.size(db) {
788 ast::OptionFixedSizeArraySize::FixedSizeArraySize(size_clause) => {
789 let mut ctx = ComputationContext::new_global(db, diagnostics, resolver);
790 let size_expr_syntax = size_clause.size(db);
791 let size = compute_expr_semantic(&mut ctx, &size_expr_syntax);
792 let const_value = resolve_const_expr_and_evaluate(
793 db,
794 &mut ctx,
795 &size,
796 size_expr_syntax.stable_ptr(db).untyped(),
797 get_usize_ty(db),
798 false,
799 );
800 if matches!(
801 const_value.long(db),
802 ConstValue::Int(_, _) | ConstValue::Generic(_) | ConstValue::ImplConstant(_)
803 ) {
804 Ok(Some(const_value))
805 } else {
806 Err(diagnostics.report(syntax.stable_ptr(db), FixedSizeArrayNonNumericSize))
807 }
808 }
809 ast::OptionFixedSizeArraySize::Empty(_) => Ok(None),
810 }
811}
812
813pub fn verify_fixed_size_array_size<'db>(
815 db: &'db dyn Database,
816 diagnostics: &mut SemanticDiagnostics<'db>,
817 size: &BigInt,
818 syntax: &ast::ExprFixedSizeArray<'db>,
819) -> Maybe<()> {
820 if size > &BigInt::from(i16::MAX) {
821 return Err(diagnostics.report(syntax.stable_ptr(db), FixedSizeArraySizeTooBig));
822 }
823 Ok(())
824}
825
826#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
827pub struct TypeInfo<'db> {
828 pub droppable: Result<ImplId<'db>, InferenceError<'db>>,
829 pub copyable: Result<ImplId<'db>, InferenceError<'db>>,
830 pub destruct_impl: Result<ImplId<'db>, InferenceError<'db>>,
831 pub panic_destruct_impl: Result<ImplId<'db>, InferenceError<'db>>,
832}
833
834pub fn get_impl_at_context<'db>(
836 db: &'db dyn Database,
837 lookup_context: ImplLookupContextId<'db>,
838 concrete_trait_id: ConcreteTraitId<'db>,
839 stable_ptr: Option<SyntaxStablePtrId<'db>>,
840) -> Result<ImplId<'db>, InferenceError<'db>> {
841 let constrains =
842 db.generic_params_type_constraints(lookup_context.long(db).generic_params.clone());
843 if constrains.is_empty() && concrete_trait_id.is_var_free(db) {
844 return solve_concrete_trait_no_constraints(db, lookup_context, concrete_trait_id);
845 }
846 let mut inference_data = InferenceData::new(InferenceId::NoContext);
847 let mut inference = inference_data.inference(db);
848 inference.conform_generic_params_type_constraints(constrains);
849 let impl_id = inference.new_impl_var(concrete_trait_id, stable_ptr, lookup_context);
852 if let Err(err_set) = inference.finalize_without_reporting() {
853 return Err(inference
854 .consume_error_without_reporting(err_set)
855 .expect("Error couldn't be already consumed"));
856 };
857 Ok(inference.rewrite(impl_id).no_err())
858}
859
860fn single_value_type(db: &dyn Database, ty: TypeId<'_>) -> Maybe<bool> {
862 Ok(match ty.long(db) {
863 TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
864 ConcreteTypeId::Struct(id) => {
865 for member in db.struct_members(id.struct_id(db))?.values() {
866 if !db.single_value_type(member.ty)? {
867 return Ok(false);
868 }
869 }
870 true
871 }
872 ConcreteTypeId::Enum(id) => {
873 let variants = db.enum_variants(id.enum_id(db))?;
874 if variants.len() != 1 {
875 return Ok(false);
876 }
877
878 db.single_value_type(
879 db.variant_semantic(id.enum_id(db), *variants.values().next().unwrap())?.ty,
880 )?
881 }
882 ConcreteTypeId::Extern(_) => false,
883 },
884 TypeLongId::Tuple(types) => {
885 for ty in types {
886 if !db.single_value_type(*ty)? {
887 return Ok(false);
888 }
889 }
890 true
891 }
892 TypeLongId::Snapshot(ty) => db.single_value_type(*ty)?,
893 TypeLongId::GenericParameter(_)
894 | TypeLongId::Var(_)
895 | TypeLongId::NumericLiteral(_)
896 | TypeLongId::Missing(_)
897 | TypeLongId::Coupon(_)
898 | TypeLongId::ImplType(_)
899 | TypeLongId::Closure(_) => false,
900 TypeLongId::FixedSizeArray { type_id, size } => {
901 db.single_value_type(*type_id)?
902 || matches!(size.long(db),
903 ConstValue::Int(value, _) if value.is_zero())
904 }
905 })
906}
907
908#[salsa::tracked]
910fn single_value_type_tracked<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> Maybe<bool> {
911 single_value_type(db, ty)
912}
913
914pub fn add_type_based_diagnostics<'db>(
916 db: &'db dyn Database,
917 diagnostics: &mut SemanticDiagnostics<'db>,
918 ty: TypeId<'db>,
919 stable_ptr: impl Into<SyntaxStablePtrId<'db>> + Copy,
920) {
921 if db.type_size_info(ty) == Ok(TypeSizeInformation::Infinite) {
922 diagnostics.report(stable_ptr, InfiniteSizeType(ty));
923 }
924 if let TypeLongId::Concrete(ConcreteTypeId::Extern(extrn)) = ty.long(db) {
925 let long_id = extrn.long(db);
926 if long_id.extern_type_id.name(db).long(db) == "Array"
927 && let [GenericArgumentId::Type(arg_ty)] = &long_id.generic_args[..]
928 && db.type_size_info(*arg_ty) == Ok(TypeSizeInformation::ZeroSized)
929 {
930 diagnostics.report(stable_ptr, ArrayOfZeroSizedElements(*arg_ty));
931 }
932 }
933}
934
935#[derive(Clone, Debug, PartialEq, Eq)]
936pub enum TypeSizeInformation {
937 Infinite,
941 ZeroSized,
943 Other,
945}
946
947fn type_size_info(db: &dyn Database, ty: TypeId<'_>) -> Maybe<TypeSizeInformation> {
949 match ty.long(db) {
950 TypeLongId::Concrete(concrete_type_id) => match concrete_type_id {
951 ConcreteTypeId::Struct(id) => {
952 if check_all_type_are_zero_sized(
953 db,
954 db.concrete_struct_members(*id)?.iter().map(|(_, member)| &member.ty),
955 )? {
956 return Ok(TypeSizeInformation::ZeroSized);
957 }
958 }
959 ConcreteTypeId::Enum(id) => {
960 for variant in &db.concrete_enum_variants(*id)? {
961 db.type_size_info(variant.ty)?;
963 }
964 }
965 ConcreteTypeId::Extern(_) => {}
966 },
967 TypeLongId::Tuple(types) => {
968 if check_all_type_are_zero_sized(db, types.iter())? {
969 return Ok(TypeSizeInformation::ZeroSized);
970 }
971 }
972 TypeLongId::Snapshot(ty) => {
973 if db.type_size_info(*ty)? == TypeSizeInformation::ZeroSized {
974 return Ok(TypeSizeInformation::ZeroSized);
975 }
976 }
977 TypeLongId::Closure(closure_ty) => {
978 if check_all_type_are_zero_sized(db, closure_ty.captured_types.iter())? {
979 return Ok(TypeSizeInformation::ZeroSized);
980 }
981 }
982 TypeLongId::Coupon(_) => return Ok(TypeSizeInformation::ZeroSized),
983 TypeLongId::GenericParameter(_)
984 | TypeLongId::Var(_)
985 | TypeLongId::NumericLiteral(_)
986 | TypeLongId::Missing(_)
987 | TypeLongId::ImplType(_) => {}
988 TypeLongId::FixedSizeArray { type_id, size } => {
989 if matches!(size.long(db), ConstValue::Int(value,_) if value.is_zero())
990 || db.type_size_info(*type_id)? == TypeSizeInformation::ZeroSized
991 {
992 return Ok(TypeSizeInformation::ZeroSized);
993 }
994 }
995 }
996 Ok(TypeSizeInformation::Other)
997}
998
999#[salsa::tracked(cycle_result=type_size_info_cycle)]
1001fn type_size_info_tracked<'db>(
1002 db: &'db dyn Database,
1003 ty: TypeId<'db>,
1004) -> Maybe<TypeSizeInformation> {
1005 type_size_info(db, ty)
1006}
1007
1008fn check_all_type_are_zero_sized<'a>(
1010 db: &dyn Database,
1011 types: impl Iterator<Item = &'a TypeId<'a>>,
1012) -> Maybe<bool> {
1013 let mut zero_sized = true;
1014 for ty in types {
1015 if db.type_size_info(*ty)? != TypeSizeInformation::ZeroSized {
1016 zero_sized = false;
1017 }
1018 }
1019 Ok(zero_sized)
1020}
1021
1022fn type_size_info_cycle<'db>(
1024 _db: &'db dyn Database,
1025 _id: salsa::Id,
1026 _ty: TypeId<'db>,
1027) -> Maybe<TypeSizeInformation> {
1028 Ok(TypeSizeInformation::Infinite)
1029}
1030
1031fn type_info<'db>(
1035 db: &'db dyn Database,
1036 lookup_context: ImplLookupContextId<'db>,
1037 ty: TypeId<'db>,
1038) -> TypeInfo<'db> {
1039 let droppable = get_impl_at_context(db, lookup_context, concrete_drop_trait(db, ty), None);
1041 let copyable = get_impl_at_context(db, lookup_context, concrete_copy_trait(db, ty), None);
1042 let destruct_impl =
1043 get_impl_at_context(db, lookup_context, concrete_destruct_trait(db, ty), None);
1044 let panic_destruct_impl =
1045 get_impl_at_context(db, lookup_context, concrete_panic_destruct_trait(db, ty), None);
1046 TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl }
1047}
1048
1049#[salsa::tracked]
1051fn type_info_tracked<'db>(
1052 db: &'db dyn Database,
1053 lookup_context: ImplLookupContextId<'db>,
1054 ty: TypeId<'db>,
1055) -> TypeInfo<'db> {
1056 type_info(db, lookup_context, ty)
1057}
1058
1059fn solve_concrete_trait_no_constraints<'db>(
1062 db: &'db dyn Database,
1063 lookup_context: ImplLookupContextId<'db>,
1064 id: ConcreteTraitId<'db>,
1065) -> Result<ImplId<'db>, InferenceError<'db>> {
1066 let mut lookup_context = lookup_context.long(db).clone();
1067 enrich_lookup_context(db, id, &mut lookup_context);
1068 let lookup_context = lookup_context.intern(db);
1069 match db.canonic_trait_solutions(
1070 CanonicalTrait { id, mappings: Default::default() },
1071 lookup_context,
1072 Default::default(),
1073 )? {
1074 SolutionSet::None => Err(InferenceError::NoImplsFound(id)),
1075 SolutionSet::Unique(solution) => Ok(solution.0),
1076 SolutionSet::Ambiguous(ambiguity) => Err(InferenceError::Ambiguity(ambiguity)),
1077 }
1078}
1079
1080fn copyable<'db>(
1082 db: &'db dyn Database,
1083 ty: TypeId<'db>,
1084) -> Result<ImplId<'db>, InferenceError<'db>> {
1085 solve_concrete_trait_no_constraints(
1086 db,
1087 ImplLookupContext::new_from_type(ty, db).intern(db),
1088 concrete_copy_trait(db, ty),
1089 )
1090}
1091
1092#[salsa::tracked]
1094fn copyable_tracked<'db>(
1095 db: &'db dyn Database,
1096 ty: TypeId<'db>,
1097) -> Result<ImplId<'db>, InferenceError<'db>> {
1098 copyable(db, ty)
1099}
1100
1101fn droppable<'db>(
1103 db: &'db dyn Database,
1104 ty: TypeId<'db>,
1105) -> Result<ImplId<'db>, InferenceError<'db>> {
1106 solve_concrete_trait_no_constraints(
1107 db,
1108 ImplLookupContext::new_from_type(ty, db).intern(db),
1109 concrete_drop_trait(db, ty),
1110 )
1111}
1112
1113#[salsa::tracked]
1115fn droppable_tracked<'db>(
1116 db: &'db dyn Database,
1117 ty: TypeId<'db>,
1118) -> Result<ImplId<'db>, InferenceError<'db>> {
1119 droppable(db, ty)
1120}
1121
1122fn priv_type_is_fully_concrete(db: &dyn Database, ty: TypeId<'_>) -> bool {
1124 match ty.long(db) {
1125 TypeLongId::Concrete(concrete_type_id) => concrete_type_id.is_fully_concrete(db),
1126 TypeLongId::Tuple(types) => types.iter().all(|ty| ty.is_fully_concrete(db)),
1127 TypeLongId::Snapshot(ty) => ty.is_fully_concrete(db),
1128 TypeLongId::GenericParameter(_)
1129 | TypeLongId::Var(_)
1130 | TypeLongId::NumericLiteral(_)
1131 | TypeLongId::Missing(_)
1132 | TypeLongId::ImplType(_) => false,
1133 TypeLongId::Coupon(function_id) => function_id.is_fully_concrete(db),
1134 TypeLongId::FixedSizeArray { type_id, size } => {
1135 type_id.is_fully_concrete(db) && size.is_fully_concrete(db)
1136 }
1137 TypeLongId::Closure(closure) => {
1138 closure.parent_function.map(|id| id.is_fully_concrete(db)).unwrap_or(true)
1139 && closure.param_tys.iter().all(|param| param.is_fully_concrete(db))
1140 && closure.ret_ty.is_fully_concrete(db)
1141 }
1142 }
1143}
1144
1145#[salsa::tracked]
1147pub fn priv_type_is_fully_concrete_tracked<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> bool {
1148 priv_type_is_fully_concrete(db, ty)
1149}
1150
1151pub fn priv_type_is_var_free<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> bool {
1152 match ty.long(db) {
1153 TypeLongId::Concrete(concrete_type_id) => concrete_type_id.is_var_free(db),
1154 TypeLongId::Tuple(types) => types.iter().all(|ty| ty.is_var_free(db)),
1155 TypeLongId::Snapshot(ty) => ty.is_var_free(db),
1156 TypeLongId::Var(_) => false,
1157 TypeLongId::NumericLiteral(_) => false,
1158 TypeLongId::GenericParameter(_) | TypeLongId::Missing(_) => true,
1159 TypeLongId::Coupon(function_id) => function_id.is_var_free(db),
1160 TypeLongId::FixedSizeArray { type_id, size } => {
1161 type_id.is_var_free(db) && size.is_var_free(db)
1162 }
1163 TypeLongId::ImplType(_) => false,
1166 TypeLongId::Closure(closure) => {
1167 chain!(&closure.captured_types, &closure.param_tys).all(|param| param.is_var_free(db))
1168 && closure.ret_ty.is_var_free(db)
1169 }
1170 }
1171}
1172
1173#[salsa::tracked]
1175pub fn priv_type_is_var_free_tracked<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> bool {
1176 priv_type_is_var_free(db, ty)
1177}
1178
1179pub fn priv_type_short_name(db: &dyn Database, ty: TypeId<'_>) -> String {
1180 match ty.long(db) {
1181 TypeLongId::Concrete(concrete_type_id) => {
1182 let mut result = concrete_type_id.generic_type(db).format(db);
1183 let mut generic_args = concrete_type_id.generic_args(db).into_iter().peekable();
1184 if generic_args.peek().is_some() {
1185 result.push_str("::<h0x");
1186 let mut hasher = Keccak256::new();
1187 for arg in generic_args {
1188 hasher.update(arg.short_name(db).as_bytes());
1189 }
1190 for c in hasher.finalize() {
1191 result.push_str(&format!("{c:x}"));
1192 }
1193 result.push('>');
1194 }
1195 result
1196 }
1197 TypeLongId::Tuple(types) => {
1198 let mut result = String::from("(h0x");
1199 let mut hasher = Keccak256::new();
1200 for ty in types {
1201 hasher.update(ty.short_name(db).as_bytes());
1202 }
1203 for c in hasher.finalize() {
1204 result.push_str(&format!("{c:x}"));
1205 }
1206 result.push(')');
1207 result
1208 }
1209 TypeLongId::Snapshot(ty) => {
1210 format!("@{}", ty.short_name(db))
1211 }
1212 TypeLongId::FixedSizeArray { type_id, size } => {
1213 format!("[{}; {:?}", type_id.short_name(db), size.debug(db))
1214 }
1215 TypeLongId::NumericLiteral(_) => "{numeric}".to_string(),
1216 other => other.format(db),
1217 }
1218}
1219
1220#[salsa::tracked]
1221pub fn priv_type_short_name_tracked<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> String {
1222 priv_type_short_name(db, ty)
1223}
1224
1225pub fn peel_snapshots<'db>(db: &'db dyn Database, ty: TypeId<'db>) -> (usize, TypeLongId<'db>) {
1228 peel_snapshots_ex(db, ty.long(db).clone())
1229}
1230
1231pub fn peel_snapshots_ex<'db>(
1233 db: &'db dyn Database,
1234 mut long_ty: TypeLongId<'db>,
1235) -> (usize, TypeLongId<'db>) {
1236 let mut n_snapshots = 0;
1237 while let TypeLongId::Snapshot(ty) = long_ty {
1238 long_ty = ty.long(db).clone();
1239 n_snapshots += 1;
1240 }
1241 (n_snapshots, long_ty)
1242}
1243
1244pub fn wrap_in_snapshots<'db>(
1246 db: &'db dyn Database,
1247 mut ty: TypeId<'db>,
1248 n_snapshots: usize,
1249) -> TypeId<'db> {
1250 for _ in 0..n_snapshots {
1251 ty = TypeLongId::Snapshot(ty).intern(db);
1252 }
1253 ty
1254}
1255
1256pub(crate) fn are_coupons_enabled(db: &dyn Database, module_id: ModuleId<'_>) -> bool {
1258 let owning_crate = module_id.owning_crate(db);
1259 let Some(config) = db.crate_config(owning_crate) else { return false };
1260 config.settings.experimental_features.coupons
1261}
1262
1263pub(crate) fn are_repr_ptrs_enabled(db: &dyn Database, module_id: ModuleId<'_>) -> bool {
1265 let owning_crate = module_id.owning_crate(db);
1266 db.crate_config(owning_crate)
1267 .is_some_and(|config| config.settings.experimental_features.repr_ptrs)
1268}
1269
1270pub trait TypesSemantic<'db>: Database {
1272 fn generic_type_generic_params(
1274 &'db self,
1275 generic_type: GenericTypeId<'db>,
1276 ) -> Maybe<&'db [semantic::GenericParam<'db>]> {
1277 match generic_type {
1278 GenericTypeId::Struct(id) => self.struct_generic_params(id),
1279 GenericTypeId::Enum(id) => self.enum_generic_params(id),
1280 GenericTypeId::Extern(id) => self.extern_type_declaration_generic_params(id),
1281 }
1282 }
1283 fn single_value_type(&'db self, ty: TypeId<'db>) -> Maybe<bool> {
1288 single_value_type_tracked(self.as_dyn_database(), ty)
1289 }
1290 fn type_size_info(&'db self, ty: TypeId<'db>) -> Maybe<TypeSizeInformation> {
1292 type_size_info_tracked(self.as_dyn_database(), ty)
1293 }
1294 fn type_info(
1296 &'db self,
1297 lookup_context: ImplLookupContextId<'db>,
1298 ty: TypeId<'db>,
1299 ) -> TypeInfo<'db> {
1300 type_info_tracked(self.as_dyn_database(), lookup_context, ty)
1301 }
1302 fn copyable(&'db self, ty: TypeId<'db>) -> Result<ImplId<'db>, InferenceError<'db>> {
1304 copyable_tracked(self.as_dyn_database(), ty)
1305 }
1306 fn droppable(&'db self, ty: TypeId<'db>) -> Result<ImplId<'db>, InferenceError<'db>> {
1308 droppable_tracked(self.as_dyn_database(), ty)
1309 }
1310}
1311impl<'db, T: Database + ?Sized> TypesSemantic<'db> for T {}
1312
1313pub trait PrivTypesSemantic<'db>: Database {
1315 fn priv_type_is_fully_concrete(&self, ty: TypeId<'db>) -> bool {
1317 priv_type_is_fully_concrete_tracked(self.as_dyn_database(), ty)
1318 }
1319 fn priv_type_is_var_free(&self, ty: TypeId<'db>) -> bool {
1321 priv_type_is_var_free_tracked(self.as_dyn_database(), ty)
1322 }
1323 fn priv_type_short_name(&self, ty: TypeId<'db>) -> String {
1325 priv_type_short_name_tracked(self.as_dyn_database(), ty)
1326 }
1327}
1328impl<'db, T: Database + ?Sized> PrivTypesSemantic<'db> for T {}