Skip to main content

cairo_lang_semantic/expr/inference/
solver.rs

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