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