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