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