1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use cairo_lang_debug::DebugWithDb;
5use cairo_lang_defs::ids::{GenericParamId, LanguageElementId};
6use cairo_lang_proc_macros::SemanticObject;
7use cairo_lang_utils::Intern;
8use cairo_lang_utils::ordered_hash_map::Entry;
9use itertools::{Itertools, chain, zip_eq};
10use salsa::Database;
11
12use super::canonic::{CanonicalImpl, CanonicalTrait, MapperError, ResultNoErrEx};
13use super::conform::InferenceConform;
14use super::infers::InferenceEmbeddings;
15use super::{
16 ImplVarTraitItemMappings, InferenceData, InferenceError, InferenceId, InferenceResult,
17 InferenceVar, LocalImplVarId,
18};
19use crate::items::constant::{ConstValue, ConstValueId, ImplConstantId};
20use crate::items::imp::{
21 ImplId, ImplImplId, ImplLongId, ImplLookupContext, ImplLookupContextId, ImplSemantic,
22 UninferredImpl, UninferredImplById, find_candidates_at_context,
23 find_closure_generated_candidate,
24};
25use crate::items::trt::TraitSemantic;
26use crate::substitution::{GenericSubstitution, SemanticRewriter};
27use crate::types::{ImplTypeById, ImplTypeId};
28use crate::{
29 ConcreteImplLongId, ConcreteTraitId, GenericArgumentId, GenericParam, TypeId, TypeLongId,
30};
31
32#[derive(Clone, PartialEq, Eq, Debug)]
34pub enum SolutionSet<'db, T> {
35 None,
36 Unique(T),
37 Ambiguous(Ambiguity<'db>),
38}
39
40unsafe impl<'db, T: salsa::Update> salsa::Update for SolutionSet<'db, T> {
42 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
43 let old_pointer = unsafe { &mut *old_pointer };
44 match (old_pointer, new_value) {
45 (SolutionSet::None, SolutionSet::None) => false,
46 (SolutionSet::Unique(u1), SolutionSet::Unique(u2)) => unsafe {
47 salsa::plumbing::UpdateDispatch::<T>::maybe_update(u1, u2)
48 },
49 (SolutionSet::Ambiguous(ambiguity), SolutionSet::Ambiguous(ambiguity2)) => unsafe {
50 salsa::plumbing::UpdateDispatch::<Ambiguity<'db>>::maybe_update(
51 ambiguity, ambiguity2,
52 )
53 },
54 (old_pointer, new_value) => {
55 *old_pointer = new_value;
56 true
57 }
58 }
59 }
60}
61
62#[derive(Clone, Debug, Eq, Hash, PartialEq, SemanticObject, salsa::Update)]
64pub enum Ambiguity<'db> {
65 MultipleImplsFound {
66 concrete_trait_id: ConcreteTraitId<'db>,
67 impls: Vec<ImplId<'db>>,
68 },
69 FreeVariable {
70 impl_id: ImplId<'db>,
71 #[dont_rewrite]
72 var: InferenceVar,
73 },
74 WillNotInfer(ConcreteTraitId<'db>),
75 NegativeImplWithUnsupportedExtractedArgs(GenericArgumentId<'db>),
76 NegativeImplWithUnsupportedGenericParam(GenericParamId<'db>),
77}
78
79impl<'db> Ambiguity<'db> {
80 pub fn format(&self, db: &dyn Database) -> String {
81 match self {
82 Ambiguity::MultipleImplsFound { concrete_trait_id, impls } => {
83 let impls_str = impls.iter().map(|imp| format!("`{}`", imp.format(db))).join(", ");
84 format!(
85 "Trait `{:?}` has multiple implementations, in: {impls_str}",
86 concrete_trait_id.debug(db)
87 )
88 }
89 Ambiguity::FreeVariable { impl_id, var: _ } => {
90 format!("Candidate impl {:?} has an unused generic parameter.", impl_id.debug(db),)
91 }
92 Ambiguity::WillNotInfer(concrete_trait_id) => {
93 format!(
94 "Cannot infer trait {:?}. First generic argument must be known.",
95 concrete_trait_id.debug(db)
96 )
97 }
98 Ambiguity::NegativeImplWithUnsupportedExtractedArgs(garg) => {
99 format!("Negative impl has an unsupported generic argument {:?}.", garg.debug(db),)
100 }
101 Ambiguity::NegativeImplWithUnsupportedGenericParam(gparam) => {
102 format!("Negative impl has an unsupported generic argument {:?}.", gparam.debug(db),)
103 }
104 }
105 }
106}
107
108pub fn canonic_trait_solutions<'db>(
111 db: &'db dyn Database,
112 canonical_trait: CanonicalTrait<'db>,
113 lookup_context: ImplLookupContextId<'db>,
114 impl_type_bounds: BTreeMap<ImplTypeById<'db>, TypeId<'db>>,
115) -> Result<SolutionSet<'db, CanonicalImpl<'db>>, InferenceError<'db>> {
116 let mut concrete_trait_id = canonical_trait.id;
117 let impl_type_bounds = Arc::new(impl_type_bounds);
118 if !concrete_trait_id.is_fully_concrete(db) && !canonical_trait.mappings.is_empty() {
121 match solve_canonical_trait(db, canonical_trait, lookup_context, impl_type_bounds.clone()) {
122 SolutionSet::None => {}
123 SolutionSet::Unique(imp) => {
124 concrete_trait_id =
125 imp.0.concrete_trait(db).expect("A solved impl must have a concrete trait");
126 }
127 SolutionSet::Ambiguous(ambiguity) => {
128 return Ok(SolutionSet::Ambiguous(ambiguity));
129 }
130 }
131 }
132 Ok(solve_canonical_trait(
134 db,
135 CanonicalTrait { id: concrete_trait_id, mappings: ImplVarTraitItemMappings::default() },
136 lookup_context,
137 impl_type_bounds,
138 ))
139}
140
141#[salsa::tracked(cycle_result=canonic_trait_solutions_cycle)]
144pub fn canonic_trait_solutions_tracked<'db>(
145 db: &'db dyn Database,
146 canonical_trait: CanonicalTrait<'db>,
147 lookup_context: ImplLookupContextId<'db>,
148 impl_type_bounds: BTreeMap<ImplTypeById<'db>, TypeId<'db>>,
149) -> Result<SolutionSet<'db, CanonicalImpl<'db>>, InferenceError<'db>> {
150 canonic_trait_solutions(db, canonical_trait, lookup_context, impl_type_bounds)
151}
152
153pub fn canonic_trait_solutions_cycle<'db>(
155 _db: &dyn Database,
156 _id: salsa::Id,
157 _canonical_trait: CanonicalTrait<'db>,
158 _lookup_context: ImplLookupContextId<'db>,
159 _impl_type_bounds: BTreeMap<ImplTypeById<'db>, TypeId<'db>>,
160) -> Result<SolutionSet<'db, CanonicalImpl<'db>>, InferenceError<'db>> {
161 Err(InferenceError::Cycle(InferenceVar::Impl(LocalImplVarId(0))))
162}
163
164pub fn enrich_lookup_context<'db>(
166 db: &'db dyn Database,
167 concrete_trait_id: ConcreteTraitId<'db>,
168 lookup_context: &mut ImplLookupContext<'db>,
169) {
170 lookup_context.insert_module(concrete_trait_id.trait_id(db).parent_module(db), db);
171 let generic_args = concrete_trait_id.generic_args(db);
172 for generic_arg in generic_args {
174 if let GenericArgumentId::Type(ty) = generic_arg {
175 enrich_lookup_context_with_ty(db, *ty, lookup_context);
176 }
177 }
178 lookup_context.strip_for_trait_id(db, concrete_trait_id.trait_id(db));
179}
180
181pub fn enrich_lookup_context_with_ty<'db>(
183 db: &'db dyn Database,
184 ty: TypeId<'db>,
185 lookup_context: &mut ImplLookupContext<'db>,
186) {
187 match ty.long(db) {
188 TypeLongId::ImplType(impl_type_id) => {
189 lookup_context.insert_impl(impl_type_id.impl_id(), db);
190 }
191 long_ty => {
192 if let Some(module_id) = long_ty.module_id(db) {
193 lookup_context.insert_module(module_id, db);
194 }
195 }
196 }
197}
198
199fn solve_canonical_trait<'db>(
202 db: &'db dyn Database,
203 canonical_trait: CanonicalTrait<'db>,
204 lookup_context: ImplLookupContextId<'db>,
205 impl_type_bounds: Arc<BTreeMap<ImplTypeById<'db>, TypeId<'db>>>,
206) -> SolutionSet<'db, CanonicalImpl<'db>> {
207 let filter = canonical_trait.id.filter(db);
208 let mut candidates = find_candidates_at_context(db, lookup_context, filter).unwrap_or_default();
209 find_closure_generated_candidate(db, canonical_trait.id)
210 .map(|candidate| candidates.insert(UninferredImplById(candidate)));
211
212 let mut unique_solution: Option<CanonicalImpl<'_>> = None;
213 for candidate in candidates.into_iter() {
214 let Ok(candidate_solution_set) = solve_candidate(
215 db,
216 &canonical_trait,
217 candidate.0,
218 lookup_context,
219 impl_type_bounds.clone(),
220 ) else {
221 continue;
222 };
223
224 let candidate_solution = match candidate_solution_set {
225 SolutionSet::None => continue,
226 SolutionSet::Unique(candidate_solution) => candidate_solution,
227 SolutionSet::Ambiguous(ambiguity) => return SolutionSet::Ambiguous(ambiguity),
228 };
229 if let Some(unique_solution) = unique_solution {
230 if unique_solution.0 != candidate_solution.0 {
234 return SolutionSet::Ambiguous(Ambiguity::MultipleImplsFound {
235 concrete_trait_id: canonical_trait.id,
236 impls: vec![unique_solution.0, candidate_solution.0],
237 });
238 }
239 }
240 unique_solution = Some(candidate_solution);
241 }
242 unique_solution.map(SolutionSet::Unique).unwrap_or(SolutionSet::None)
243}
244
245fn solve_candidate<'db>(
247 db: &'db dyn Database,
248 canonical_trait: &CanonicalTrait<'db>,
249 candidate: UninferredImpl<'db>,
250 lookup_context: ImplLookupContextId<'db>,
251 impl_type_bounds: Arc<BTreeMap<ImplTypeById<'db>, TypeId<'db>>>,
252) -> InferenceResult<SolutionSet<'db, CanonicalImpl<'db>>> {
253 let Ok(candidate_concrete_trait) = candidate.concrete_trait(db) else {
254 return Err(super::ErrorSet);
255 };
256 let candidate_final = matches!(candidate, UninferredImpl::GenericParam(_))
259 && candidate_concrete_trait.is_var_free(db)
260 || candidate_concrete_trait.is_fully_concrete(db);
261 let target_final = canonical_trait.id.is_var_free(db);
262 let mut lite_inference = LiteInference::new(db);
263 if candidate_final && target_final && candidate_concrete_trait != canonical_trait.id {
264 return Err(super::ErrorSet);
265 }
266
267 let mut res = lite_inference.can_conform_generic_args(
268 (candidate_concrete_trait.generic_args(db), candidate_final),
269 (canonical_trait.id.generic_args(db), target_final),
270 );
271
272 if matches!(candidate, UninferredImpl::GenericParam(_))
274 && !lite_inference.substitution.is_empty()
275 {
276 return Err(super::ErrorSet);
277 }
278
279 if res == CanConformResult::Accepted {
281 let Ok(trait_types) = db.trait_types(canonical_trait.id.trait_id(db)) else {
282 return Err(super::ErrorSet);
283 };
284 if !trait_types.is_empty() && !canonical_trait.mappings.types.is_empty() {
285 res = CanConformResult::InferenceRequired;
286 }
287 }
288
289 let mut lookup_context = lookup_context.long(db).clone();
291 lookup_context.insert_lookup_scope(db, &candidate);
292 let lookup_context = lookup_context.intern(db);
293 if res == CanConformResult::Rejected {
294 return Err(super::ErrorSet);
295 } else if CanConformResult::Accepted == res {
296 match candidate {
297 UninferredImpl::Def(impl_def_id) => {
298 let imp_generic_params =
299 db.impl_def_generic_params(impl_def_id).map_err(|_| super::ErrorSet)?;
300
301 match lite_inference.infer_generic_assignment(
302 imp_generic_params,
303 lookup_context,
304 impl_type_bounds.clone(),
305 ) {
306 Ok(SolutionSet::None) => {
307 return Ok(SolutionSet::None);
308 }
309 Ok(SolutionSet::Ambiguous(ambiguity)) => {
310 return Ok(SolutionSet::Ambiguous(ambiguity));
311 }
312 Ok(SolutionSet::Unique(generic_args)) => {
313 let concrete_impl =
314 ConcreteImplLongId { impl_def_id, generic_args }.intern(db);
315 let impl_id = ImplLongId::Concrete(concrete_impl).intern(db);
316 return Ok(SolutionSet::Unique(CanonicalImpl(impl_id)));
317 }
318 _ => {}
319 }
320 }
321 UninferredImpl::GenericParam(generic_param_id) => {
322 let impl_id = ImplLongId::GenericParameter(generic_param_id).intern(db);
323 return Ok(SolutionSet::Unique(CanonicalImpl(impl_id)));
324 }
325 UninferredImpl::ImplAlias(_) => {}
327 UninferredImpl::ImplImpl(_) | UninferredImpl::GeneratedImpl(_) => {}
328 }
329 }
330
331 let mut inference_data: InferenceData<'_> = InferenceData::new(InferenceId::Canonical);
332 let mut inference = inference_data.inference(db);
333 inference.data.impl_type_bounds = impl_type_bounds;
334 let (canonical_trait, canonical_embedding) = canonical_trait.embed(&mut inference);
335
336 if let UninferredImpl::GeneratedImpl(imp) = candidate {
339 inference.conform_traits(imp.long(db).concrete_trait, canonical_trait.id)?;
340 }
341
342 let candidate_impl =
344 inference.infer_impl(candidate, canonical_trait.id, lookup_context, None)?;
345 for (trait_type, ty) in canonical_trait.mappings.types.iter() {
346 let mapped_ty =
347 inference.reduce_impl_ty(ImplTypeId::new(candidate_impl, *trait_type, db))?;
348
349 inference.conform_ty(mapped_ty, *ty)?;
351 }
352 for (trait_const, const_id) in canonical_trait.mappings.constants.iter() {
353 let mapped_const_id = inference.reduce_impl_constant(ImplConstantId::new(
354 candidate_impl,
355 *trait_const,
356 db,
357 ))?;
358 inference.conform_const(mapped_const_id, *const_id)?;
360 }
361
362 for (trait_impl, impl_id) in canonical_trait.mappings.impls.iter() {
363 let mapped_impl_id =
364 inference.reduce_impl_impl(ImplImplId::new(candidate_impl, *trait_impl, db))?;
365 inference.conform_impl(mapped_impl_id, *impl_id)?;
367 }
368
369 let mut inference = inference_data.inference(db);
370 let solution_set = inference.solution_set()?;
371 Ok(match solution_set {
372 SolutionSet::None => SolutionSet::None,
373 SolutionSet::Ambiguous(ambiguity) => SolutionSet::Ambiguous(ambiguity),
374 SolutionSet::Unique(_) => {
375 let candidate_impl = inference.rewrite(candidate_impl).no_err();
376 match CanonicalImpl::canonicalize(db, candidate_impl, &canonical_embedding) {
377 Ok(canonical_impl) => SolutionSet::Unique(canonical_impl),
378 Err(MapperError(var)) => {
379 return Ok(SolutionSet::Ambiguous(Ambiguity::FreeVariable {
380 impl_id: candidate_impl,
381 var,
382 }));
383 }
384 }
385 }
386 })
387}
388
389#[derive(Clone, Copy, Debug, PartialEq, Eq)]
391enum CanConformResult {
392 Accepted,
393 InferenceRequired,
394 Rejected,
395}
396
397impl CanConformResult {
398 fn fold(iter: impl IntoIterator<Item = CanConformResult>) -> CanConformResult {
399 let mut res = CanConformResult::Accepted; for item in iter {
401 match item {
402 CanConformResult::Rejected => return CanConformResult::Rejected,
403 CanConformResult::Accepted => continue,
404 CanConformResult::InferenceRequired => {
405 res = CanConformResult::InferenceRequired;
406 }
407 }
408 }
409 res }
411}
412struct LiteInference<'db> {
415 db: &'db dyn Database,
416 substitution: GenericSubstitution<'db>,
417}
418
419impl<'db> LiteInference<'db> {
420 fn new(db: &'db dyn Database) -> Self {
421 LiteInference { db, substitution: GenericSubstitution::default() }
422 }
423
424 fn infer_generic_assignment(
427 &mut self,
428 params: &[GenericParam<'db>],
429 lookup_context: ImplLookupContextId<'db>,
430 impl_type_bounds: Arc<BTreeMap<ImplTypeById<'db>, TypeId<'db>>>,
431 ) -> InferenceResult<SolutionSet<'db, Vec<GenericArgumentId<'db>>>> {
432 let mut generic_args = Vec::with_capacity(params.len());
433 for param in params {
434 match param {
435 GenericParam::Type(generic_param_type) => {
436 if self.substitution.contains_key(&generic_param_type.id) {
437 generic_args.push(*self.substitution.get(&generic_param_type.id).unwrap());
438 } else {
439 return Err(super::ErrorSet);
442 }
443 }
444 GenericParam::Const(generic_param_const) => {
445 if self.substitution.contains_key(&generic_param_const.id) {
446 generic_args.push(*self.substitution.get(&generic_param_const.id).unwrap());
447 } else {
448 return Err(super::ErrorSet);
451 }
452 }
453 GenericParam::Impl(generic_param_impl) => {
454 if !generic_param_impl.type_constraints.is_empty() {
455 return Err(super::ErrorSet);
456 }
457 if self.substitution.contains_key(&generic_param_impl.id) {
458 generic_args.push(*self.substitution.get(&generic_param_impl.id).unwrap());
459 continue;
460 }
461
462 let Ok(Ok(imp_concrete_trait_id)) =
463 self.substitution.substitute(self.db, generic_param_impl.concrete_trait)
464 else {
465 return Err(super::ErrorSet);
466 };
467 let canonical_trait = CanonicalTrait {
468 id: imp_concrete_trait_id,
469 mappings: ImplVarTraitItemMappings::default(),
470 };
471 let mut inner_context = lookup_context.long(self.db).clone();
472 enrich_lookup_context(self.db, imp_concrete_trait_id, &mut inner_context);
473 let Ok(solution) = self.db.canonic_trait_solutions(
474 canonical_trait,
475 inner_context.intern(self.db),
476 (*impl_type_bounds).clone(),
477 ) else {
478 return Err(super::ErrorSet);
479 };
480 match solution {
481 SolutionSet::None => return Ok(SolutionSet::None),
482 SolutionSet::Unique(imp) => {
483 self.substitution
484 .insert(generic_param_impl.id, GenericArgumentId::Impl(imp.0));
485 generic_args.push(GenericArgumentId::Impl(imp.0));
486 }
487 SolutionSet::Ambiguous(ambiguity) => {
488 return Ok(SolutionSet::Ambiguous(ambiguity));
489 }
490 }
491 }
492 GenericParam::NegImpl(_) => return Err(super::ErrorSet),
493 }
494 }
495 Ok(SolutionSet::Unique(generic_args))
496 }
497
498 fn can_conform_generic_args(
502 &mut self,
503 (candidate_args, candidate_final): (&[GenericArgumentId<'db>], bool),
504 (target_args, target_final): (&[GenericArgumentId<'db>], bool),
505 ) -> CanConformResult {
506 CanConformResult::fold(zip_eq(candidate_args, target_args).map(
507 |(candidate_arg, target_arg)| {
508 self.can_conform_generic_arg(
509 (*candidate_arg, candidate_final),
510 (*target_arg, target_final),
511 )
512 },
513 ))
514 }
515
516 fn can_conform_generic_arg(
519 &mut self,
520 (candidate_arg, mut candidate_final): (GenericArgumentId<'db>, bool),
521 (target_arg, mut target_final): (GenericArgumentId<'db>, bool),
522 ) -> CanConformResult {
523 if candidate_arg == target_arg {
524 return CanConformResult::Accepted;
525 }
526 candidate_final = candidate_final || candidate_arg.is_fully_concrete(self.db);
527 target_final = target_final || target_arg.is_var_free(self.db);
528 if candidate_final && target_final {
529 return CanConformResult::Rejected;
530 }
531 match (candidate_arg, target_arg) {
532 (GenericArgumentId::Type(candidate), GenericArgumentId::Type(target)) => {
533 self.can_conform_ty((candidate, candidate_final), (target, target_final))
534 }
535 (GenericArgumentId::Constant(candidate), GenericArgumentId::Constant(target)) => {
536 self.can_conform_const((candidate, candidate_final), (target, target_final))
537 }
538 (GenericArgumentId::Impl(candidate), GenericArgumentId::Impl(target)) => {
539 self.can_conform_impl((candidate, candidate_final), (target, target_final))
540 }
541 (GenericArgumentId::NegImpl(_), GenericArgumentId::NegImpl(_)) => {
542 CanConformResult::InferenceRequired
543 }
544 _ => CanConformResult::Rejected,
545 }
546 }
547
548 fn can_conform_ty(
551 &mut self,
552 (candidate_ty, mut candidate_final): (TypeId<'db>, bool),
553 (target_ty, mut target_final): (TypeId<'db>, bool),
554 ) -> CanConformResult {
555 if candidate_ty == target_ty {
556 return CanConformResult::Accepted;
557 }
558 candidate_final = candidate_final || candidate_ty.is_fully_concrete(self.db);
559 target_final = target_final || target_ty.is_var_free(self.db);
560 if candidate_final && target_final {
561 return CanConformResult::Rejected;
562 }
563 let target_long_ty = target_ty.long(self.db);
564
565 if let TypeLongId::Var(_) = target_long_ty {
566 return CanConformResult::InferenceRequired;
567 }
568
569 let long_ty_candidate = candidate_ty.long(self.db);
570
571 match (long_ty_candidate, target_long_ty) {
572 (TypeLongId::Concrete(candidate), TypeLongId::Concrete(target)) => {
573 if candidate.generic_type(self.db) != target.generic_type(self.db) {
574 return CanConformResult::Rejected;
575 }
576
577 self.can_conform_generic_args(
578 (&candidate.generic_args(self.db), candidate_final),
579 (&target.generic_args(self.db), target_final),
580 )
581 }
582 (TypeLongId::Concrete(_), _) => CanConformResult::Rejected,
583 (TypeLongId::Tuple(candidate_tys), TypeLongId::Tuple(target_tys)) => {
584 if candidate_tys.len() != target_tys.len() {
585 return CanConformResult::Rejected;
586 }
587
588 CanConformResult::fold(zip_eq(candidate_tys, target_tys).map(
589 |(candidate_subty, target_subty)| {
590 self.can_conform_ty(
591 (*candidate_subty, candidate_final),
592 (*target_subty, target_final),
593 )
594 },
595 ))
596 }
597 (TypeLongId::Tuple(_), _) => CanConformResult::Rejected,
598 (TypeLongId::Closure(candidate), TypeLongId::Closure(target)) => {
599 if candidate.params_location != target.params_location {
600 return CanConformResult::Rejected;
601 }
602
603 let params_check = CanConformResult::fold(
604 zip_eq(candidate.param_tys.clone(), target.param_tys.clone()).map(
605 |(candidate_subty, target_subty)| {
606 self.can_conform_ty(
607 (candidate_subty, candidate_final),
608 (target_subty, target_final),
609 )
610 },
611 ),
612 );
613 if params_check == CanConformResult::Rejected {
614 return CanConformResult::Rejected;
615 }
616 let captured_types_check = CanConformResult::fold(
617 zip_eq(candidate.captured_types.clone(), target.captured_types.clone()).map(
618 |(candidate_subty, target_subty)| {
619 self.can_conform_ty(
620 (candidate_subty, candidate_final),
621 (target_subty, target_final),
622 )
623 },
624 ),
625 );
626 if captured_types_check == CanConformResult::Rejected {
627 return CanConformResult::Rejected;
628 }
629 let return_type_check = self.can_conform_ty(
630 (candidate.ret_ty, candidate_final),
631 (target.ret_ty, target_final),
632 );
633 if return_type_check == CanConformResult::Rejected {
634 return CanConformResult::Rejected;
635 }
636 if params_check == CanConformResult::InferenceRequired
637 || captured_types_check == CanConformResult::InferenceRequired
638 || return_type_check == CanConformResult::InferenceRequired
639 {
640 return CanConformResult::InferenceRequired;
641 }
642 CanConformResult::Accepted
643 }
644 (TypeLongId::Closure(_), _) => CanConformResult::Rejected,
645 (
646 TypeLongId::FixedSizeArray { type_id: candidate_type_id, size: candidate_size },
647 TypeLongId::FixedSizeArray { type_id: target_type_id, size: target_size },
648 ) => CanConformResult::fold([
649 self.can_conform_const(
650 (*candidate_size, candidate_final),
651 (*target_size, target_final),
652 ),
653 self.can_conform_ty(
654 (*candidate_type_id, candidate_final),
655 (*target_type_id, target_final),
656 ),
657 ]),
658 (TypeLongId::FixedSizeArray { type_id: _, size: _ }, _) => CanConformResult::Rejected,
659 (TypeLongId::Snapshot(candidate_inner_ty), TypeLongId::Snapshot(target_inner_ty)) => {
660 self.can_conform_ty(
661 (*candidate_inner_ty, candidate_final),
662 (*target_inner_ty, target_final),
663 )
664 }
665 (TypeLongId::Snapshot(_), _) => CanConformResult::Rejected,
666 (TypeLongId::GenericParameter(param), _) => {
667 let mut res = CanConformResult::Accepted;
668 match self.substitution.entry(*param) {
670 Entry::Occupied(entry) => {
671 if let GenericArgumentId::Type(existing_ty) = entry.get() {
672 if *existing_ty != target_ty {
673 res = CanConformResult::Rejected;
674 }
675 if !existing_ty.is_var_free(self.db) {
676 return CanConformResult::InferenceRequired;
677 }
678 } else {
679 res = CanConformResult::Rejected;
680 }
681 }
682 Entry::Vacant(e) => {
683 e.insert(GenericArgumentId::Type(target_ty));
684 }
685 }
686
687 if target_ty.is_var_free(self.db) {
688 res
689 } else {
690 CanConformResult::InferenceRequired
691 }
692 }
693 (
694 TypeLongId::Var(_)
695 | TypeLongId::ImplType(_)
696 | TypeLongId::Missing(_)
697 | TypeLongId::Coupon(_),
698 _,
699 ) => CanConformResult::InferenceRequired,
700 }
701 }
702
703 fn can_conform_impl(
706 &mut self,
707 (candidate_impl, mut candidate_final): (ImplId<'db>, bool),
708 (target_impl, mut target_final): (ImplId<'db>, bool),
709 ) -> CanConformResult {
710 let long_impl_trait = target_impl.long(self.db);
711 if candidate_impl == target_impl {
712 return CanConformResult::Accepted;
713 }
714 candidate_final = candidate_final || candidate_impl.is_fully_concrete(self.db);
715 target_final = target_final || target_impl.is_var_free(self.db);
716 if candidate_final && target_final {
717 return CanConformResult::Rejected;
718 }
719 if let ImplLongId::ImplVar(_) = long_impl_trait {
720 return CanConformResult::InferenceRequired;
721 }
722 match (candidate_impl.long(self.db), long_impl_trait) {
723 (ImplLongId::Concrete(candidate), ImplLongId::Concrete(target)) => {
724 let candidate = candidate.long(self.db);
725 let target = target.long(self.db);
726 if candidate.impl_def_id != target.impl_def_id {
727 return CanConformResult::Rejected;
728 }
729 let candidate_args = candidate.generic_args.clone();
730 let target_args = target.generic_args.clone();
731 self.can_conform_generic_args(
732 (&candidate_args, candidate_final),
733 (&target_args, target_final),
734 )
735 }
736 (ImplLongId::Concrete(_), _) => CanConformResult::Rejected,
737 (ImplLongId::GenericParameter(param), _) => {
738 let mut res = CanConformResult::Accepted;
739 match self.substitution.entry(*param) {
741 Entry::Occupied(entry) => {
742 if let GenericArgumentId::Impl(existing_impl) = entry.get() {
743 if *existing_impl != target_impl {
744 res = CanConformResult::Rejected;
745 }
746 if !existing_impl.is_var_free(self.db) {
747 return CanConformResult::InferenceRequired;
748 }
749 } else {
750 res = CanConformResult::Rejected;
751 }
752 }
753 Entry::Vacant(e) => {
754 e.insert(GenericArgumentId::Impl(target_impl));
755 }
756 }
757
758 if target_impl.is_var_free(self.db) {
759 res
760 } else {
761 CanConformResult::InferenceRequired
762 }
763 }
764 (
765 ImplLongId::ImplVar(_)
766 | ImplLongId::ImplImpl(_)
767 | ImplLongId::SelfImpl(_)
768 | ImplLongId::GeneratedImpl(_),
769 _,
770 ) => CanConformResult::InferenceRequired,
771 }
772 }
773
774 fn can_conform_const(
777 &mut self,
778 (candidate_id, mut candidate_final): (ConstValueId<'db>, bool),
779 (target_id, mut target_final): (ConstValueId<'db>, bool),
780 ) -> CanConformResult {
781 if candidate_id == target_id {
782 return CanConformResult::Accepted;
783 }
784 candidate_final = candidate_final || candidate_id.is_fully_concrete(self.db);
785 target_final = target_final || target_id.is_var_free(self.db);
786 if candidate_final && target_final {
787 return CanConformResult::Rejected;
788 }
789 let target_long_const = target_id.long(self.db);
790 if let ConstValue::Var(_, _) = target_long_const {
791 return CanConformResult::InferenceRequired;
792 }
793 match (candidate_id.long(self.db), target_long_const) {
794 (
795 ConstValue::Int(big_int, type_id),
796 ConstValue::Int(target_big_int, target_type_id),
797 ) => {
798 if big_int != target_big_int {
799 return CanConformResult::Rejected;
800 }
801 self.can_conform_ty((*type_id, candidate_final), (*target_type_id, target_final))
802 }
803 (ConstValue::Int(_, _), _) => CanConformResult::Rejected,
804 (
805 ConstValue::Struct(const_values, type_id),
806 ConstValue::Struct(target_const_values, target_type_id),
807 ) => {
808 if const_values.len() != target_const_values.len() {
809 return CanConformResult::Rejected;
810 };
811 CanConformResult::fold(chain!(
812 [self.can_conform_ty(
813 (*type_id, candidate_final),
814 (*target_type_id, target_final)
815 )],
816 zip_eq(const_values, target_const_values).map(
817 |(const_value, target_const_value)| {
818 self.can_conform_const(
819 (*const_value, candidate_final),
820 (*target_const_value, target_final),
821 )
822 }
823 )
824 ))
825 }
826 (ConstValue::Struct(_, _), _) => CanConformResult::Rejected,
827
828 (
829 ConstValue::Enum(concrete_variant, const_value),
830 ConstValue::Enum(target_concrete_variant, target_const_value),
831 ) => CanConformResult::fold([
832 self.can_conform_ty(
833 (concrete_variant.ty, candidate_final),
834 (target_concrete_variant.ty, target_final),
835 ),
836 self.can_conform_const(
837 (*const_value, candidate_final),
838 (*target_const_value, target_final),
839 ),
840 ]),
841 (ConstValue::Enum(_, _), _) => CanConformResult::Rejected,
842 (ConstValue::NonZero(const_value), ConstValue::NonZero(target_const_value)) => self
843 .can_conform_const(
844 (*const_value, candidate_final),
845 (*target_const_value, target_final),
846 ),
847 (ConstValue::NonZero(_), _) => CanConformResult::Rejected,
848 (ConstValue::Generic(param), _) => {
849 let mut res = CanConformResult::Accepted;
850 match self.substitution.entry(*param) {
851 Entry::Occupied(entry) => {
852 if let GenericArgumentId::Constant(existing_const) = entry.get() {
853 if *existing_const != target_id {
854 res = CanConformResult::Rejected;
855 }
856
857 if !existing_const.is_var_free(self.db) {
858 return CanConformResult::InferenceRequired;
859 }
860 } else {
861 res = CanConformResult::Rejected;
862 }
863 }
864 Entry::Vacant(e) => {
865 e.insert(GenericArgumentId::Constant(target_id));
866 }
867 }
868 if target_id.is_var_free(self.db) {
869 res
870 } else {
871 CanConformResult::InferenceRequired
872 }
873 }
874 (ConstValue::ImplConstant(_) | ConstValue::Var(_, _) | ConstValue::Missing(_), _) => {
875 CanConformResult::InferenceRequired
876 }
877 }
878 }
879}
880
881pub trait SemanticSolver<'db>: Database {
883 fn canonic_trait_solutions(
885 &'db self,
886 canonical_trait: CanonicalTrait<'db>,
887 lookup_context: ImplLookupContextId<'db>,
888 impl_type_bounds: BTreeMap<ImplTypeById<'db>, TypeId<'db>>,
889 ) -> Result<SolutionSet<'db, CanonicalImpl<'db>>, InferenceError<'db>> {
890 canonic_trait_solutions_tracked(
891 self.as_dyn_database(),
892 canonical_trait,
893 lookup_context,
894 impl_type_bounds,
895 )
896 }
897}
898impl<'db, T: Database + ?Sized> SemanticSolver<'db> for T {}