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