chalk_recursive/
solve.rs

1use super::combine;
2use super::fulfill::Fulfill;
3use crate::fixed_point::Minimums;
4use crate::UCanonicalGoal;
5use chalk_ir::could_match::CouldMatch;
6use chalk_ir::fold::TypeFoldable;
7use chalk_ir::interner::{HasInterner, Interner};
8use chalk_ir::{
9    Canonical, ClausePriority, DomainGoal, Fallible, Floundered, Goal, GoalData, InEnvironment,
10    NoSolution, ProgramClause, ProgramClauseData, Substitution, UCanonical,
11};
12use chalk_solve::clauses::program_clauses_that_could_match;
13use chalk_solve::debug_span;
14use chalk_solve::infer::InferenceTable;
15use chalk_solve::{Guidance, RustIrDatabase, Solution};
16use tracing::{debug, instrument};
17
18pub(super) trait SolveDatabase<I: Interner>: Sized {
19    fn solve_goal(
20        &mut self,
21        goal: UCanonical<InEnvironment<Goal<I>>>,
22        minimums: &mut Minimums,
23        should_continue: impl std::ops::Fn() -> bool + Clone,
24    ) -> Fallible<Solution<I>>;
25
26    fn max_size(&self) -> usize;
27
28    fn interner(&self) -> I;
29
30    fn db(&self) -> &dyn RustIrDatabase<I>;
31}
32
33/// The `solve_iteration` method -- implemented for any type that implements
34/// `SolveDb`.
35pub(super) trait SolveIteration<I: Interner>: SolveDatabase<I> {
36    /// Executes one iteration of the recursive solver, computing the current
37    /// solution to the given canonical goal. This is used as part of a loop in
38    /// the case of cyclic goals.
39    #[instrument(level = "debug", skip(self, should_continue))]
40    fn solve_iteration(
41        &mut self,
42        canonical_goal: &UCanonicalGoal<I>,
43        minimums: &mut Minimums,
44        should_continue: impl std::ops::Fn() -> bool + Clone,
45    ) -> Fallible<Solution<I>> {
46        if !should_continue() {
47            return Ok(Solution::Ambig(Guidance::Unknown));
48        }
49
50        let UCanonical {
51            universes,
52            canonical:
53                Canonical {
54                    binders,
55                    value: InEnvironment { environment, goal },
56                },
57        } = canonical_goal.clone();
58
59        match goal.data(self.interner()) {
60            GoalData::DomainGoal(domain_goal) => {
61                let canonical_goal = UCanonical {
62                    universes,
63                    canonical: Canonical {
64                        binders,
65                        value: InEnvironment {
66                            environment,
67                            goal: domain_goal.clone(),
68                        },
69                    },
70                };
71
72                // "Domain" goals (i.e., leaf goals that are Rust-specific) are
73                // always solved via some form of implication. We can either
74                // apply assumptions from our environment (i.e. where clauses),
75                // or from the lowered program, which includes fallback
76                // clauses. We try each approach in turn:
77
78                let prog_solution = {
79                    debug_span!("prog_clauses");
80
81                    self.solve_from_clauses(&canonical_goal, minimums, should_continue)
82                };
83                debug!(?prog_solution);
84
85                prog_solution
86            }
87
88            _ => {
89                let canonical_goal = UCanonical {
90                    universes,
91                    canonical: Canonical {
92                        binders,
93                        value: InEnvironment { environment, goal },
94                    },
95                };
96
97                self.solve_via_simplification(&canonical_goal, minimums, should_continue)
98            }
99        }
100    }
101}
102
103impl<S, I> SolveIteration<I> for S
104where
105    S: SolveDatabase<I>,
106    I: Interner,
107{
108}
109
110/// Helper methods for `solve_iteration`, private to this module.
111trait SolveIterationHelpers<I: Interner>: SolveDatabase<I> {
112    #[instrument(level = "debug", skip(self, minimums, should_continue))]
113    fn solve_via_simplification(
114        &mut self,
115        canonical_goal: &UCanonicalGoal<I>,
116        minimums: &mut Minimums,
117        should_continue: impl std::ops::Fn() -> bool + Clone,
118    ) -> Fallible<Solution<I>> {
119        let (infer, subst, goal) = self.new_inference_table(canonical_goal);
120        match Fulfill::new_with_simplification(self, infer, subst, goal) {
121            Ok(fulfill) => fulfill.solve(minimums, should_continue),
122            Err(e) => Err(e),
123        }
124    }
125
126    /// See whether we can solve a goal by implication on any of the given
127    /// clauses. If multiple such solutions are possible, we attempt to combine
128    /// them.
129    fn solve_from_clauses(
130        &mut self,
131        canonical_goal: &UCanonical<InEnvironment<DomainGoal<I>>>,
132        minimums: &mut Minimums,
133        should_continue: impl std::ops::Fn() -> bool + Clone,
134    ) -> Fallible<Solution<I>> {
135        let mut clauses = vec![];
136
137        let db = self.db();
138        let could_match = |c: &ProgramClause<I>| {
139            c.could_match(
140                db.interner(),
141                db.unification_database(),
142                &canonical_goal.canonical.value.goal,
143            )
144        };
145        clauses.extend(db.custom_clauses().into_iter().filter(could_match));
146        match program_clauses_that_could_match(db, canonical_goal) {
147            Ok(goal_clauses) => clauses.extend(goal_clauses.into_iter().filter(could_match)),
148            Err(Floundered) => {
149                return Ok(Solution::Ambig(Guidance::Unknown));
150            }
151        }
152
153        let (infer, subst, goal) = self.new_inference_table(canonical_goal);
154        clauses.extend(
155            db.program_clauses_for_env(&goal.environment)
156                .iter(db.interner())
157                .cloned()
158                .filter(could_match),
159        );
160
161        let mut cur_solution = None;
162        for program_clause in clauses {
163            debug_span!("solve_from_clauses", clause = ?program_clause);
164
165            let ProgramClauseData(implication) = program_clause.data(self.interner());
166            let infer = infer.clone();
167            let subst = subst.clone();
168            let goal = goal.clone();
169            let res = match Fulfill::new_with_clause(self, infer, subst, goal, implication) {
170                Ok(fulfill) => (
171                    fulfill.solve(minimums, should_continue.clone()),
172                    implication.skip_binders().priority,
173                ),
174                Err(e) => (Err(e), ClausePriority::High),
175            };
176
177            if let (Ok(solution), priority) = res {
178                debug!(?solution, ?priority, "Ok");
179                cur_solution = Some(match cur_solution {
180                    None => (solution, priority),
181                    Some((cur, cur_priority)) => combine::with_priorities(
182                        self.interner(),
183                        &canonical_goal.canonical.value.goal,
184                        cur,
185                        cur_priority,
186                        solution,
187                        priority,
188                    ),
189                });
190            } else {
191                debug!("Error");
192            }
193
194            if let Some((cur_solution, _)) = &cur_solution {
195                if cur_solution.is_trivial_and_always_true(self.interner()) {
196                    break;
197                }
198            }
199        }
200
201        if let Some((s, _)) = cur_solution {
202            debug!("solve_from_clauses: result = {:?}", s);
203            Ok(s)
204        } else {
205            debug!("solve_from_clauses: error");
206            Err(NoSolution)
207        }
208    }
209
210    fn new_inference_table<T: TypeFoldable<I> + HasInterner<Interner = I> + Clone>(
211        &self,
212        ucanonical_goal: &UCanonical<InEnvironment<T>>,
213    ) -> (InferenceTable<I>, Substitution<I>, InEnvironment<T>) {
214        let (infer, subst, canonical_goal) = InferenceTable::from_canonical(
215            self.interner(),
216            ucanonical_goal.universes,
217            ucanonical_goal.canonical.clone(),
218        );
219        (infer, subst, canonical_goal)
220    }
221}
222
223impl<S, I> SolveIterationHelpers<I> for S
224where
225    S: SolveDatabase<I>,
226    I: Interner,
227{
228}