Skip to main content

cairo_lang_semantic/expr/inference/
solver.rs

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/// A generic solution set for an inference constraint system.
33#[derive(Clone, PartialEq, Eq, Debug)]
34pub enum SolutionSet<'db, T> {
35    None,
36    Unique(T),
37    Ambiguous(Ambiguity<'db>),
38}
39
40// Somewhat taken from the salsa::Update derive macro.
41unsafe 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/// Describes the kinds of inference ambiguities.
63#[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
108/// Implementation of [SemanticSolver::canonic_trait_solutions].
109/// Assumes the lookup context is already enriched by [enrich_lookup_context].
110pub 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 the trait is not fully concrete, we might be able to use the trait's items to find a
119    // more concrete trait.
120    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    // Solve the trait without the trait items, so we'd be able to find conflicting impls.
133    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/// Query implementation of [SemanticSolver::canonic_trait_solutions].
142/// Assumes the lookup context is already enriched by [enrich_lookup_context].
143#[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
153/// Cycle handling for [canonic_trait_solutions].
154pub 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
164/// Adds the defining module of the trait and the generic arguments to the lookup context.
165pub 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    // Add the defining module of the generic args to the lookup.
173    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
181/// Adds the defining module of the type to the lookup context.
182pub 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
199/// Attempts to solve a `canonical_trait`. Will try to find candidates in the given
200/// `lookup_context`.
201fn 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            // There might be multiple unique solutions from different candidates that are
231            // solved to the same impl id (e.g. finding it near the trait, and
232            // through an impl alias). This is valid.
233            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
245/// Attempts to solve `candidate` as the requested `canonical_trait`.
246fn 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    // If the candidate is fully concrete, or it's a generic which is var free, there is nothing
257    // to substitute. A generic param may not be var free, if it contains impl types.
258    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 the candidate is a generic param, its trait is final and not substituted.
273    if matches!(candidate, UninferredImpl::GenericParam(_))
274        && !lite_inference.substitution.is_empty()
275    {
276        return Err(super::ErrorSet);
277    }
278
279    // If the trait has trait types, we default to using inference.
280    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    // Add the defining module of the candidate to the lookup.
290    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            // TODO(TomerStarkware): Try to solve for impl alias without inference.
326            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 the closure params are not var free, we cannot infer the negative impl.
337    // We use the canonical trait to concretize the closure params.
338    if let UninferredImpl::GeneratedImpl(imp) = candidate {
339        inference.conform_traits(imp.long(db).concrete_trait, canonical_trait.id)?;
340    }
341
342    // Instantiate the candidate in the inference table.
343    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        // Conform the candidate's type to the trait's type.
350        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        // Conform the candidate's constant to the trait's constant.
359        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        // Conform the candidate's impl to the trait's impl.
366        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/// Enum for the result of `can_conform`.
390#[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; // Start with a default value of Accepted
400        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 // Return the final result
410    }
411}
412/// An inference without 'vars' that can be used to solve canonical traits which do not contain
413/// 'vars' or associated items.
414struct 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    /// Tries to infer the generic arguments of the trait from the given params.
425    /// If the inference fails (i.e., requires full inference), returns an error.
426    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                        // If the type is not in the substitution, we cannot solve it without
440                        // inference.
441                        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                        // If the const is not in the substitution, we cannot solve it without
449                        // inference.
450                        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    /// Checks if the generic arguments of the candidate could be conformed to the generic args of
499    /// the trait and if the trait or the candidate contain vars (which would require solving
500    /// using inference).
501    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    /// Checks if a [GenericArgumentId] of the candidate could be conformed to a [GenericArgumentId]
517    /// of the trait.
518    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    /// Checks if a generic arg [TypeId] of the candidate could be conformed to a generic arg
549    /// [TypeId] of the trait.
550    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                // if param not in substitution add it otherwise make sure it equal target_ty
669                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    /// Checks if a generic arg [ImplId] of the candidate could be conformed to a generic arg
704    /// [ImplId] of the trait.
705    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                // if param not in substitution add it otherwise make sure it equal target_ty
740                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    /// Checks if a generic arg [ConstValueId] of the candidate could be conformed to a generic arg
775    /// [ConstValueId] of the trait.
776    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
881/// Trait for solver-related semantic queries.
882pub trait SemanticSolver<'db>: Database {
883    /// Returns the solution set for a canonical trait.
884    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 {}