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