1 use crate::RustIrDatabase
;
2 use chalk_derive
::HasInterner
;
3 use chalk_ir
::interner
::Interner
;
10 /// A (possible) solution for a proposed goal.
11 #[derive(Clone, Debug, PartialEq, Eq, HasInterner)]
12 pub enum Solution
<I
: Interner
> {
13 /// The goal indeed holds, and there is a unique value for all existential
14 /// variables. In this case, we also record a set of lifetime constraints
15 /// which must also hold for the goal to be valid.
16 Unique(Canonical
<ConstrainedSubst
<I
>>),
18 /// The goal may be provable in multiple ways, but regardless we may have some guidance
19 /// for type inference. In this case, we don't return any lifetime
20 /// constraints, since we have not "committed" to any particular solution
25 /// When a goal holds ambiguously (e.g., because there are multiple possible
26 /// solutions), we issue a set of *guidance* back to type inference.
27 #[derive(Clone, Debug, PartialEq, Eq)]
28 pub enum Guidance
<I
: Interner
> {
29 /// The existential variables *must* have the given values if the goal is
30 /// ever to hold, but that alone isn't enough to guarantee the goal will
32 Definite(Canonical
<Substitution
<I
>>),
34 /// There are multiple plausible values for the existentials, but the ones
35 /// here are suggested as the preferred choice heuristically. These should
36 /// be used for inference fallback only.
37 Suggested(Canonical
<Substitution
<I
>>),
39 /// There's no useful information to feed back to type inference
43 impl<I
: Interner
> Solution
<I
> {
44 /// There are multiple candidate solutions, which may or may not agree on
45 /// the values for existential variables; attempt to combine them. This
46 /// operation does not depend on the order of its arguments.
48 /// This actually isn't as precise as it could be, in two ways:
50 /// a. It might be that while there are multiple distinct candidates, they
51 /// all agree about *some things*. To be maximally precise, we would
52 /// compute the intersection of what they agree on. It's not clear though
53 /// that this is actually what we want Rust's inference to do, and it's
54 /// certainly not what it does today.
56 /// b. There might also be an ambiguous candidate and a successful candidate,
57 /// both with the same refined-goal. In that case, we could probably claim
58 /// success, since if the conditions of the ambiguous candidate were met,
59 /// we know the success would apply. Example: `?0: Clone` yields ambiguous
60 /// candidate `Option<?0>: Clone` and successful candidate `Option<?0>:
63 /// But you get the idea.
64 pub fn combine(self, other
: Solution
<I
>, interner
: I
) -> Solution
<I
> {
65 use self::Guidance
::*;
71 // Special case hack: if one solution is "true" without any constraints,
72 // that is always the combined result.
74 // This is not as general as it could be: ideally, if we had one solution
75 // that is Unique with a simpler substitution than the other one, or region constraints
76 // which are a subset, we'd combine them.
77 if self.is_trivial_and_always_true(interner
) {
80 if other
.is_trivial_and_always_true(interner
) {
86 self.display(interner
),
87 other
.display(interner
)
90 // Otherwise, always downgrade to Ambig:
92 let guidance
= match (self.into_guidance(), other
.into_guidance()) {
93 (Definite(ref subst1
), Definite(ref subst2
)) if subst1
== subst2
=> {
94 Definite(subst1
.clone())
96 (Suggested(ref subst1
), Suggested(ref subst2
)) if subst1
== subst2
=> {
97 Suggested(subst1
.clone())
101 Solution
::Ambig(guidance
)
104 pub fn is_trivial_and_always_true(&self, interner
: I
) -> bool
{
106 Solution
::Unique(constrained_subst
) => {
107 constrained_subst
.value
.subst
.is_identity_subst(interner
)
108 && constrained_subst
.value
.constraints
.is_empty(interner
)
110 Solution
::Ambig(_
) => false,
114 /// View this solution purely in terms of type inference guidance
115 pub fn into_guidance(self) -> Guidance
<I
> {
117 Solution
::Unique(constrained
) => Guidance
::Definite(Canonical
{
118 value
: constrained
.value
.subst
,
119 binders
: constrained
.binders
,
121 Solution
::Ambig(guidance
) => guidance
,
125 /// Extract a constrained substitution from this solution, even if ambiguous.
126 pub fn constrained_subst(&self, interner
: I
) -> Option
<Canonical
<ConstrainedSubst
<I
>>> {
128 Solution
::Unique(ref constrained
) => Some(constrained
.clone()),
129 Solution
::Ambig(Guidance
::Definite(ref canonical
))
130 | Solution
::Ambig(Guidance
::Suggested(ref canonical
)) => {
131 let value
= ConstrainedSubst
{
132 subst
: canonical
.value
.clone(),
133 constraints
: Constraints
::empty(interner
),
137 binders
: canonical
.binders
.clone(),
140 Solution
::Ambig(_
) => None
,
144 /// Determine whether this solution contains type information that *must*
145 /// hold, and returns the subst in that case.
146 pub fn definite_subst(&self, interner
: I
) -> Option
<Canonical
<ConstrainedSubst
<I
>>> {
148 Solution
::Unique(constrained
) => Some(constrained
.clone()),
149 Solution
::Ambig(Guidance
::Definite(canonical
)) => {
150 let value
= ConstrainedSubst
{
151 subst
: canonical
.value
.clone(),
152 constraints
: Constraints
::empty(interner
),
156 binders
: canonical
.binders
.clone(),
163 pub fn is_unique(&self) -> bool
{
164 matches
!(*self, Solution
::Unique(..))
167 pub fn is_ambig(&self) -> bool
{
168 matches
!(*self, Solution
::Ambig(_
))
171 pub fn display(&self, interner
: I
) -> SolutionDisplay
<'_
, I
> {
179 pub struct SolutionDisplay
<'a
, I
: Interner
> {
180 solution
: &'a Solution
<I
>,
184 impl<'a
, I
: Interner
> fmt
::Display
for SolutionDisplay
<'a
, I
> {
186 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> Result
<(), fmt
::Error
> {
187 let SolutionDisplay { solution, interner }
= self;
189 // If a `Unique` solution has no associated data, omit the trailing semicolon.
190 // This makes blessed test output nicer to read.
191 Solution
::Unique(Canonical { binders, value: ConstrainedSubst { subst, constraints }
} )
192 if interner
.constraints_data(constraints
.interned()).is_empty()
193 && interner
.substitution_data(subst
.interned()).is_empty()
194 && interner
.canonical_var_kinds_data(binders
.interned()).is_empty()
195 => write
!(f
, "Unique"),
197 Solution
::Unique(constrained
) => write
!(f
, "Unique; {}", constrained
.display(*interner
)),
199 Solution
::Ambig(Guidance
::Definite(subst
)) => write
!(
201 "Ambiguous; definite substitution {}",
202 subst
.display(*interner
)
204 Solution
::Ambig(Guidance
::Suggested(subst
)) => write
!(
206 "Ambiguous; suggested substitution {}",
207 subst
.display(*interner
)
209 Solution
::Ambig(Guidance
::Unknown
) => write
!(f
, "Ambiguous; no inference guidance"),
215 pub enum SubstitutionResult
<S
> {
221 impl<S
> SubstitutionResult
<S
> {
222 pub fn as_ref(&self) -> SubstitutionResult
<&S
> {
224 SubstitutionResult
::Definite(subst
) => SubstitutionResult
::Definite(subst
),
225 SubstitutionResult
::Ambiguous(subst
) => SubstitutionResult
::Ambiguous(subst
),
226 SubstitutionResult
::Floundered
=> SubstitutionResult
::Floundered
,
229 pub fn map
<U
, F
: FnOnce(S
) -> U
>(self, f
: F
) -> SubstitutionResult
<U
> {
231 SubstitutionResult
::Definite(subst
) => SubstitutionResult
::Definite(f(subst
)),
232 SubstitutionResult
::Ambiguous(subst
) => SubstitutionResult
::Ambiguous(f(subst
)),
233 SubstitutionResult
::Floundered
=> SubstitutionResult
::Floundered
,
238 impl<S
: fmt
::Display
> fmt
::Display
for SubstitutionResult
<S
> {
239 fn fmt(&self, fmt
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
241 SubstitutionResult
::Definite(subst
) => write
!(fmt
, "{}", subst
),
242 SubstitutionResult
::Ambiguous(subst
) => write
!(fmt
, "Ambiguous({})", subst
),
243 SubstitutionResult
::Floundered
=> write
!(fmt
, "Floundered"),
248 /// Finds the solution to "goals", or trait queries -- i.e., figures
249 /// out what sets of types implement which traits. Also, between
250 /// queries, this struct stores the cached state from previous solver
251 /// attempts, which can then be re-used later.
252 pub trait Solver
<I
: Interner
>
256 /// Attempts to solve the given goal, which must be in canonical
257 /// form. Returns a unique solution (if one exists). This will do
258 /// only as much work towards `goal` as it has to (and that work
259 /// is cached for future attempts).
263 /// - `program` -- defines the program clauses in scope.
264 /// - **Important:** You must supply the same set of program clauses
265 /// each time you invoke `solve`, as otherwise the cached data may be
267 /// - `goal` the goal to solve
271 /// - `None` is the goal cannot be proven.
272 /// - `Some(solution)` if we succeeded in finding *some* answers,
273 /// although `solution` may reflect ambiguity and unknowns.
276 program
: &dyn RustIrDatabase
<I
>,
277 goal
: &UCanonical
<InEnvironment
<Goal
<I
>>>,
278 ) -> Option
<Solution
<I
>>;
280 /// Attempts to solve the given goal, which must be in canonical
281 /// form. Returns a unique solution (if one exists). This will do
282 /// only as much work towards `goal` as it has to (and that work
283 /// is cached for future attempts). In addition, the solving of the
284 /// goal can be limited by returning `false` from `should_continue`.
288 /// - `program` -- defines the program clauses in scope.
289 /// - **Important:** You must supply the same set of program clauses
290 /// each time you invoke `solve`, as otherwise the cached data may be
292 /// - `goal` the goal to solve
293 /// - `should_continue` if `false` is returned, the no further solving
294 /// will be done. A `Guidance(Suggested(...))` will be returned a
295 /// `Solution`, using any answers that were generated up to that point.
299 /// - `None` is the goal cannot be proven.
300 /// - `Some(solution)` if we succeeded in finding *some* answers,
301 /// although `solution` may reflect ambiguity and unknowns.
304 program
: &dyn RustIrDatabase
<I
>,
305 goal
: &UCanonical
<InEnvironment
<Goal
<I
>>>,
306 should_continue
: &dyn std
::ops
::Fn() -> bool
,
307 ) -> Option
<Solution
<I
>>;
309 /// Attempts to solve the given goal, which must be in canonical
310 /// form. Provides multiple solutions to function `f`. This will do
311 /// only as much work towards `goal` as it has to (and that work
312 /// is cached for future attempts).
316 /// - `program` -- defines the program clauses in scope.
317 /// - **Important:** You must supply the same set of program clauses
318 /// each time you invoke `solve`, as otherwise the cached data may be
320 /// - `goal` the goal to solve
321 /// - `f` -- function to proceed solution. New solutions will be generated
322 /// while function returns `true`.
323 /// - first argument is solution found
324 /// - second argument is the next solution present
325 /// - returns true if next solution should be handled
329 /// - `true` all solutions were processed with the function.
330 /// - `false` the function returned `false` and solutions were interrupted.
333 program
: &dyn RustIrDatabase
<I
>,
334 goal
: &UCanonical
<InEnvironment
<Goal
<I
>>>,
335 f
: &mut dyn FnMut(SubstitutionResult
<Canonical
<ConstrainedSubst
<I
>>>, bool
) -> bool
,
338 /// A convenience method for when one doesn't need the actual solution,
339 /// only whether or not one exists.
340 fn has_unique_solution(
342 program
: &dyn RustIrDatabase
<I
>,
343 goal
: &UCanonical
<InEnvironment
<Goal
<I
>>>,
345 match self.solve(program
, goal
) {
346 Some(sol
) => sol
.is_unique(),