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