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