Skip to content

Commit 759034a

Browse files
authored
Merge pull request #45 from scalexm/cannot-prove
Add `CannotProve` solution
2 parents 1726210 + faae05a commit 759034a

File tree

5 files changed

+76
-89
lines changed

5 files changed

+76
-89
lines changed

src/solve/fulfill.rs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ impl Outcome {
2323
}
2424
}
2525

26-
pub struct UnifyOutcome {
27-
pub ambiguous: bool,
28-
}
29-
3026
/// A goal that must be resolved
3127
#[derive(Clone, Debug, PartialEq, Eq)]
3228
enum Obligation {
@@ -53,6 +49,7 @@ struct PositiveSolution {
5349
enum NegativeSolution {
5450
Refuted,
5551
Ambiguous,
52+
CannotProve,
5653
}
5754

5855
/// A `Fulfill` is where we actually break down complex goals, instantiate
@@ -77,8 +74,9 @@ pub struct Fulfill<'s> {
7774
constraints: HashSet<InEnvironment<Constraint>>,
7875

7976
/// Record that a goal has been processed that can neither be proved nor
80-
/// refuted, and thus the overall solution cannot be `Unique`
81-
ambiguous: bool,
77+
/// refuted. In such a case the solution will be either `CannotProve`, or `Err`
78+
/// in the case where some other goal leads to an error.
79+
cannot_prove: bool,
8280
}
8381

8482
impl<'s> Fulfill<'s> {
@@ -88,7 +86,7 @@ impl<'s> Fulfill<'s> {
8886
infer: InferenceTable::new(),
8987
obligations: vec![],
9088
constraints: HashSet::new(),
91-
ambiguous: false,
89+
cannot_prove: false,
9290
}
9391
}
9492

@@ -119,19 +117,19 @@ impl<'s> Fulfill<'s> {
119117
///
120118
/// Wraps `InferenceTable::unify`; any resulting normalizations are added
121119
/// into our list of pending obligations with the given environment.
122-
pub fn unify<T>(&mut self, environment: &Arc<Environment>, a: &T, b: &T) -> Result<UnifyOutcome>
120+
pub fn unify<T>(&mut self, environment: &Arc<Environment>, a: &T, b: &T) -> Result<()>
123121
where T: ?Sized + Zip + Debug
124122
{
125-
let UnificationResult { goals, constraints, ambiguous } =
123+
let UnificationResult { goals, constraints, cannot_prove } =
126124
self.infer.unify(environment, a, b)?;
127125
debug!("unify({:?}, {:?}) succeeded", a, b);
128126
debug!("unify: goals={:?}", goals);
129127
debug!("unify: constraints={:?}", constraints);
130-
debug!("unify: ambiguous={:?}", ambiguous);
128+
debug!("unify: cannot_prove={:?}", cannot_prove);
131129
self.constraints.extend(constraints);
132130
self.obligations.extend(goals.into_iter().map(Obligation::Prove));
133-
self.ambiguous = self.ambiguous || ambiguous;
134-
Ok(UnifyOutcome { ambiguous })
131+
self.cannot_prove = self.cannot_prove || cannot_prove;
132+
Ok(())
135133
}
136134

137135
/// Create obligations for the given goal in the given environment. This may
@@ -223,10 +221,11 @@ impl<'s> Fulfill<'s> {
223221
}
224222

225223
// Negate the result
226-
if let Ok(solution) = self.solver.solve_closed_goal(canonicalized.quantified.value) {
224+
if let Ok(solution) = self.solver.solve_closed_goal(canonicalized.quantified.value) {
227225
match solution {
228226
Solution::Unique(_) => Err("refutation failed")?,
229227
Solution::Ambig(_) => Ok(NegativeSolution::Ambiguous),
228+
Solution::CannotProve => Ok(NegativeSolution::CannotProve),
230229
}
231230
} else {
232231
Ok(NegativeSolution::Refuted)
@@ -301,7 +300,7 @@ impl<'s> Fulfill<'s> {
301300
// directly.
302301
assert!(obligations.is_empty());
303302
while let Some(obligation) = self.obligations.pop() {
304-
let ambiguous = match obligation {
303+
let (ambiguous, cannot_prove) = match obligation {
305304
Obligation::Prove(ref wc) => {
306305
let PositiveSolution { free_vars, solution } = self.prove(wc)?;
307306

@@ -312,24 +311,30 @@ impl<'s> Fulfill<'s> {
312311
}
313312
}
314313

315-
solution.is_ambig()
314+
(solution.is_ambig(), solution.cannot_be_proven())
316315
}
317316
Obligation::Refute(ref goal) => {
318-
self.refute(goal)? == NegativeSolution::Ambiguous
317+
let answer = self.refute(goal)?;
318+
(answer == NegativeSolution::Ambiguous, answer == NegativeSolution::CannotProve)
319319
}
320320
};
321321

322322
if ambiguous {
323323
debug!("ambiguous result: {:?}", obligation);
324324
obligations.push(obligation);
325325
}
326+
327+
// If one of the obligations cannot be proven then the whole goal
328+
// cannot be proven either, unless another obligation leads to an error
329+
// in which case the function will bail out normally.
330+
self.cannot_prove = self.cannot_prove || cannot_prove;
326331
}
327332

328333
self.obligations.extend(obligations.drain(..));
329334
debug!("end of round, {} obligations left", self.obligations.len());
330335
}
331336

332-
// At the end of this process, `self.to_prove` should have
337+
// At the end of this process, `self.obligations` should have
333338
// all of the ambiguous obligations, and `obligations` should
334339
// be empty.
335340
assert!(obligations.is_empty());
@@ -346,7 +351,12 @@ impl<'s> Fulfill<'s> {
346351
/// the outcome of type inference by updating the replacements it provides.
347352
pub fn solve(mut self, subst: Substitution) -> Result<Solution> {
348353
let outcome = self.fulfill()?;
349-
if outcome.is_complete() && !self.ambiguous {
354+
355+
if self.cannot_prove {
356+
return Ok(Solution::CannotProve);
357+
}
358+
359+
if outcome.is_complete() {
350360
// No obligations remain, so we have definitively solved our goals,
351361
// and the current inference state is the unique way to solve them.
352362

src/solve/infer/unify.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct Unifier<'t> {
3434
snapshot: InferenceSnapshot,
3535
goals: Vec<InEnvironment<LeafGoal>>,
3636
constraints: Vec<InEnvironment<Constraint>>,
37-
ambiguous: bool,
37+
cannot_prove: bool,
3838
}
3939

4040
#[derive(Debug)]
@@ -46,7 +46,7 @@ pub struct UnificationResult {
4646
/// neither confirm nor deny their equality, since we interpret the
4747
/// unification request as talking about *all possible
4848
/// substitutions*. Instead, we return an ambiguous result.
49-
pub ambiguous: bool,
49+
pub cannot_prove: bool,
5050
}
5151

5252
impl<'t> Unifier<'t> {
@@ -58,7 +58,7 @@ impl<'t> Unifier<'t> {
5858
snapshot: snapshot,
5959
goals: vec![],
6060
constraints: vec![],
61-
ambiguous: false,
61+
cannot_prove: false,
6262
}
6363
}
6464

@@ -67,7 +67,7 @@ impl<'t> Unifier<'t> {
6767
Ok(UnificationResult {
6868
goals: self.goals,
6969
constraints: self.constraints,
70-
ambiguous: self.ambiguous,
70+
cannot_prove: self.cannot_prove,
7171
})
7272
}
7373

@@ -118,13 +118,13 @@ impl<'t> Unifier<'t> {
118118
if apply1.name.is_for_all() || apply2.name.is_for_all() {
119119
// we're being asked to prove something like `!0 = !1`
120120
// or `!0 = i32`. We interpret this as being asked
121-
// whether that holds *for all subtitutions*. Thus, the
122-
// answer is always *maybe* (ambiguous). That means we get:
121+
// whether that holds *for all subtitutions*. Thus, we
122+
// cannot prove the goal. That means we get:
123123
//
124-
// forall<T, U> { T = U } // Ambig
125-
// forall<T, U> { not { T = U } } // Ambig
124+
// forall<T, U> { T = U } // CannotProve
125+
// forall<T, U> { not { T = U } } // CannotProve
126126

127-
self.ambiguous = true;
127+
self.cannot_prove = true;
128128
return Ok(())
129129
} else {
130130
bail!("cannot equate `{:?}` and `{:?}`", apply1.name, apply2.name);

src/solve/mod.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@ pub enum Solution {
1616
/// which must also hold for the goal to be valid.
1717
Unique(Canonical<ConstrainedSubst>),
1818

19-
/// The goal may or may not hold, but regardless we may have some guidance
19+
/// The goal may be provable in multiple ways, but regardless we may have some guidance
2020
/// for type inference. In this case, we don't return any lifetime
2121
/// constraints, since we have not "committed" to any particular solution
2222
/// yet.
2323
Ambig(Guidance),
24+
25+
/// There is no instantiation of the existentials for which we could prove this goal
26+
/// to be true. Nonetheless, the goal may yet be true for some instantiations of the
27+
/// universals. In other words, this goal is neither true nor false.
28+
CannotProve,
2429
}
2530

2631
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -66,8 +71,8 @@ impl Solution {
6671
use self::Guidance::*;
6772

6873
if self == other { return self }
69-
if self.is_empty_unique() { return self }
70-
if other.is_empty_unique() { return other }
74+
if other.cannot_be_proven() { return self }
75+
if self.cannot_be_proven() { return other }
7176

7277
// Otherwise, always downgrade to Ambig:
7378

@@ -93,8 +98,8 @@ impl Solution {
9398
use self::Guidance::*;
9499

95100
if self == other { return self }
96-
if self.is_empty_unique() { return self }
97-
if other.is_empty_unique() { return other }
101+
if other.cannot_be_proven() { return self }
102+
if self.cannot_be_proven() { return other }
98103

99104
// Otherwise, always downgrade to Ambig:
100105

@@ -113,6 +118,9 @@ impl Solution {
113118
use self::Guidance::*;
114119

115120
if self == other { return self }
121+
if other.cannot_be_proven() { return self }
122+
if self.cannot_be_proven() { return other }
123+
116124
if let Solution::Ambig(guidance) = self {
117125
match guidance {
118126
Definite(subst) | Suggested(subst) => Solution::Ambig(Suggested(subst)),
@@ -132,7 +140,8 @@ impl Solution {
132140
binders: constrained.binders,
133141
})
134142
}
135-
Solution::Ambig(guidance) => guidance
143+
Solution::Ambig(guidance) => guidance,
144+
Solution::CannotProve => Guidance::Unknown,
136145
}
137146
}
138147

@@ -148,7 +157,7 @@ impl Solution {
148157
};
149158
Some(Canonical { value, binders: canonical.binders.clone() })
150159
}
151-
Solution::Ambig(_) => None,
160+
Solution::Ambig(_) | Solution::CannotProve => None,
152161
}
153162
}
154163

@@ -169,13 +178,9 @@ impl Solution {
169178
}
170179
}
171180

172-
/// We use emptiness, rather than triviality, to deduce that alternative
173-
/// solutions **cannot meaningfully differ**
174-
pub fn is_empty_unique(&self) -> bool {
181+
pub fn cannot_be_proven(&self) -> bool {
175182
match *self {
176-
Solution::Unique(ref subst) => {
177-
subst.binders.is_empty() && subst.value.subst.is_empty()
178-
}
183+
Solution::CannotProve => true,
179184
_ => false,
180185
}
181186
}
@@ -198,6 +203,9 @@ impl fmt::Display for Solution {
198203
Solution::Ambig(Guidance::Unknown) => {
199204
write!(f, "Ambiguous; no inference guidance")
200205
}
206+
Solution::CannotProve => {
207+
write!(f, "CannotProve")
208+
}
201209
}
202210
}
203211
}

src/solve/solver.rs

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -115,21 +115,21 @@ impl Solver {
115115
.environment
116116
.elaborated_clauses(&self.program)
117117
.map(DomainGoal::into_program_clause);
118-
let env_solution = self.solve_from_clauses(&binders, &value, env_clauses, true);
118+
let env_solution = self.solve_from_clauses(&binders, &value, env_clauses);
119119

120120
let prog_clauses: Vec<_> = self.program.program_clauses.iter()
121121
.cloned()
122122
.filter(|clause| !clause.fallback_clause)
123123
.collect();
124-
let prog_solution = self.solve_from_clauses(&binders, &value, prog_clauses, false);
124+
let prog_solution = self.solve_from_clauses(&binders, &value, prog_clauses);
125125

126126
// These fallback clauses are used when we're sure we'll never
127127
// reach Unique via another route
128128
let fallback: Vec<_> = self.program.program_clauses.iter()
129129
.cloned()
130130
.filter(|clause| clause.fallback_clause)
131131
.collect();
132-
let fallback_solution = self.solve_from_clauses(&binders, &value, fallback, false);
132+
let fallback_solution = self.solve_from_clauses(&binders, &value, fallback);
133133

134134
// Now that we have all the outcomes, we attempt to combine
135135
// them. Here, we apply a heuristic (also found in rustc): if we
@@ -172,8 +172,7 @@ impl Solver {
172172
&mut self,
173173
binders: &[ParameterKind<UniverseIndex>],
174174
goal: &InEnvironment<DomainGoal>,
175-
clauses: C,
176-
from_env: bool,
175+
clauses: C
177176
) -> Result<Solution>
178177
where
179178
C: IntoIterator<Item = ProgramClause>,
@@ -182,7 +181,7 @@ impl Solver {
182181
for ProgramClause { implication, .. } in clauses {
183182
debug_heading!("clause={:?}", implication);
184183

185-
let res = self.solve_via_implication(binders, goal.clone(), implication, from_env);
184+
let res = self.solve_via_implication(binders, goal.clone(), implication);
186185
if let Ok(solution) = res {
187186
debug!("ok: solution={:?}", solution);
188187
cur_solution = Some(
@@ -203,8 +202,7 @@ impl Solver {
203202
&mut self,
204203
binders: &[ParameterKind<UniverseIndex>],
205204
goal: InEnvironment<DomainGoal>,
206-
clause: Binders<ProgramClauseImplication>,
207-
from_env: bool,
205+
clause: Binders<ProgramClauseImplication>
208206
) -> Result<Solution> {
209207
let mut fulfill = Fulfill::new(self);
210208
let subst = Substitution::from_binders(&binders);
@@ -213,42 +211,7 @@ impl Solver {
213211
let ProgramClauseImplication { consequence, conditions} =
214212
fulfill.instantiate_in(goal.environment.universe, clause.binders, &clause.value);
215213

216-
// first, see if the implication's conclusion might solve our goal
217-
if fulfill.unify(&goal.environment, &goal.goal, &consequence)?.ambiguous && from_env {
218-
// here, using this environmental assumption would require unifying a skolemized
219-
// variable, which can only appear in an *environment*, never the
220-
// program. We view the program as providing a "closed world" of
221-
// possible types and impls, and thus we can safely abandon this
222-
// route of proving our goal.
223-
//
224-
// You can see this at work in the following example, where the
225-
// assumption in the `if` is ignored since it cannot actually be applied.
226-
//
227-
// ```
228-
// program {
229-
// struct i32 { }
230-
// struct u32 { }
231-
//
232-
// trait Foo<T> { }
233-
//
234-
// impl<T> Foo<i32> for T { }
235-
// }
236-
//
237-
// goal {
238-
// forall<T> {
239-
// exists<U> {
240-
// if (i32: Foo<T>) {
241-
// T: Foo<U>
242-
// }
243-
// }
244-
// }
245-
// } yields {
246-
// "Unique"
247-
// }
248-
// ```
249-
250-
Err("using the implication would require changing a forall variable")?;
251-
}
214+
fulfill.unify(&goal.environment, &goal.goal, &consequence)?;
252215

253216
// if so, toss in all of its premises
254217
for condition in conditions {

0 commit comments

Comments
 (0)