Skip to content

Commit 42a83d9

Browse files
authored
Merge pull request #317 from jackh726/tests_refactor
Refactor tests in `slg` into the general test infra
2 parents 4d51a98 + 854cada commit 42a83d9

22 files changed

+1243
-1854
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ edition = "2018"
1313
bench = []
1414

1515
[dependencies]
16-
diff = "0.1.11"
1716
docopt = "1.0.0"
1817
itertools = "0.8.0"
1918
lalrpop-intern = "0.15.1"

chalk-engine/src/context.rs

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,17 @@ pub trait Context: Clone + Debug {
174174
) -> Self::CanonicalAnswerSubst;
175175

176176
fn goal_from_goal_in_environment(goal: &Self::GoalInEnvironment) -> &Self::Goal;
177+
178+
/// Returns a identity substitution.
179+
fn identity_constrained_subst(
180+
goal: &Self::UCanonicalGoalInEnvironment,
181+
) -> Self::CanonicalConstrainedSubst;
177182
}
178183

179184
pub trait ContextOps<C: Context>: Sized + Clone + Debug + AggregateOps<C> {
180185
/// True if this is a coinductive goal -- e.g., proving an auto trait.
181186
fn is_coinductive(&self, goal: &C::UCanonicalGoalInEnvironment) -> bool;
182187

183-
/// Returns a identity substitution.
184-
fn identity_constrained_subst(
185-
&self,
186-
goal: &C::UCanonicalGoalInEnvironment,
187-
) -> C::CanonicalConstrainedSubst;
188-
189188
/// Returns the set of program clauses that might apply to
190189
/// `goal`. (This set can be over-approximated, naturally.)
191190
///
@@ -246,7 +245,7 @@ pub trait ContextOps<C: Context>: Sized + Clone + Debug + AggregateOps<C> {
246245
pub trait AggregateOps<C: Context> {
247246
fn make_solution(
248247
&self,
249-
root_goal: &C::CanonicalGoalInEnvironment,
248+
root_goal: &C::UCanonicalGoalInEnvironment,
250249
answers: impl AnswerStream<C>,
251250
) -> Option<C::Solution>;
252251
}
@@ -380,9 +379,58 @@ pub trait ResolventOps<C: Context> {
380379
) -> Fallible<()>;
381380
}
382381

382+
pub enum AnswerResult<C: Context> {
383+
/// The next available answer.
384+
Answer(CompleteAnswer<C>),
385+
386+
/// No answer could be returned because there are no more solutions.
387+
NoMoreSolutions,
388+
389+
/// No answer could be returned because the goal has floundered.
390+
Floundered,
391+
}
392+
393+
impl<C: Context> AnswerResult<C> {
394+
pub fn is_answer(&self) -> bool {
395+
match self {
396+
Self::Answer(_) => true,
397+
_ => false,
398+
}
399+
}
400+
401+
pub fn answer(self) -> CompleteAnswer<C> {
402+
match self {
403+
Self::Answer(answer) => answer,
404+
_ => panic!("Not an answer."),
405+
}
406+
}
407+
408+
pub fn is_no_more_solutions(&self) -> bool {
409+
match self {
410+
Self::NoMoreSolutions => true,
411+
_ => false,
412+
}
413+
}
414+
}
415+
416+
impl<C: Context> Debug for AnswerResult<C> {
417+
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
418+
match self {
419+
AnswerResult::Answer(answer) => write!(fmt, "{:?}", answer),
420+
AnswerResult::Floundered => write!(fmt, "Floundered"),
421+
AnswerResult::NoMoreSolutions => write!(fmt, "None"),
422+
}
423+
}
424+
}
425+
383426
pub trait AnswerStream<C: Context> {
384-
fn peek_answer(&mut self) -> Option<CompleteAnswer<C>>;
385-
fn next_answer(&mut self) -> Option<CompleteAnswer<C>>;
427+
/// Gets the next answer for a given goal, but doesn't increment the answer index.
428+
/// Calling this or `next_answer` again will give the same answer.
429+
fn peek_answer(&mut self) -> AnswerResult<C>;
430+
431+
/// Gets the next answer for a given goal, incrementing the answer index.
432+
/// Calling this or `peek_answer` again will give the next answer.
433+
fn next_answer(&mut self) -> AnswerResult<C>;
386434

387435
/// Invokes `test` with each possible future answer, returning true immediately
388436
/// if we find any answer for which `test` returns true.

chalk-engine/src/forest.rs

Lines changed: 44 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::context::prelude::*;
2+
use crate::context::AnswerResult;
23
use crate::context::AnswerStream;
34
use crate::logic::RootSearchFail;
45
use crate::stack::{Stack, StackIndex};
56
use crate::table::AnswerIndex;
67
use crate::tables::Tables;
7-
use crate::{CompleteAnswer, TableIndex};
8+
use crate::TableIndex;
9+
use std::fmt::Display;
810

911
pub struct Forest<C: Context> {
1012
context: C,
@@ -31,53 +33,6 @@ impl<C: Context> Forest<C> {
3133
&self.context
3234
}
3335

34-
/// Finds the first N answers, looping as much as needed to get
35-
/// them. Returns `None` if the result flounders.
36-
///
37-
/// Thanks to subgoal abstraction and so forth, this should always
38-
/// terminate.
39-
///
40-
/// # Panics
41-
///
42-
/// Panics if a negative cycle was detected.
43-
pub fn force_answers(
44-
&mut self,
45-
context: &impl ContextOps<C>,
46-
goal: C::UCanonicalGoalInEnvironment,
47-
num_answers: usize,
48-
) -> Option<Vec<CompleteAnswer<C>>> {
49-
let table = self.get_or_create_table_for_ucanonical_goal(context, goal);
50-
let mut answers = Vec::with_capacity(num_answers);
51-
let mut count = 0;
52-
for _ in 0..num_answers {
53-
loop {
54-
match self.root_answer(context, table, AnswerIndex::from(count)) {
55-
Ok(answer) => {
56-
answers.push(answer.clone());
57-
break;
58-
}
59-
Err(RootSearchFail::InvalidAnswer) => {
60-
count += 1;
61-
}
62-
Err(RootSearchFail::Floundered) => return None,
63-
Err(RootSearchFail::QuantumExceeded) => continue,
64-
Err(RootSearchFail::NoMoreSolutions) => return Some(answers),
65-
Err(RootSearchFail::NegativeCycle) => {
66-
// Negative cycles *ought* to be avoided by construction. Hence panic
67-
// if we find one, as that likely indicates a problem in the chalk-solve
68-
// lowering rules. (In principle, we could propagate this error out,
69-
// and let chalk-solve do the asserting, but that seemed like it would
70-
// complicate the function signature more than it's worth.)
71-
panic!("negative cycle was detected");
72-
}
73-
}
74-
}
75-
count += 1;
76-
}
77-
78-
Some(answers)
79-
}
80-
8136
/// Returns a "solver" for a given goal in the form of an
8237
/// iterator. Each time you invoke `next`, it will do the work to
8338
/// extract one more answer. These answers are cached in between
@@ -105,7 +60,7 @@ impl<C: Context> Forest<C> {
10560
context: &impl ContextOps<C>,
10661
goal: &C::UCanonicalGoalInEnvironment,
10762
) -> Option<C::Solution> {
108-
context.make_solution(C::canonical(&goal), self.iter_answers(context, goal))
63+
context.make_solution(&goal, self.iter_answers(context, goal))
10964
}
11065

11166
/// Solves a given goal, producing the solution. This will do only
@@ -116,18 +71,28 @@ impl<C: Context> Forest<C> {
11671
&mut self,
11772
context: &impl ContextOps<C>,
11873
goal: &C::UCanonicalGoalInEnvironment,
119-
mut f: impl FnMut(C::CanonicalConstrainedSubst, bool) -> bool,
74+
mut f: impl FnMut(SubstitutionResult<C::CanonicalConstrainedSubst>, bool) -> bool,
12075
) -> bool {
12176
let mut answers = self.iter_answers(context, goal);
122-
while let Some(answer) = answers.next_answer() {
123-
if !f(
124-
context.constrained_subst_from_answer(answer),
125-
answers.peek_answer().is_some(),
126-
) {
77+
loop {
78+
let subst = match answers.next_answer() {
79+
AnswerResult::Answer(answer) => {
80+
if !answer.ambiguous {
81+
SubstitutionResult::Definite(context.constrained_subst_from_answer(answer))
82+
} else {
83+
SubstitutionResult::Ambiguous(context.constrained_subst_from_answer(answer))
84+
}
85+
}
86+
AnswerResult::Floundered => SubstitutionResult::Floundered,
87+
AnswerResult::NoMoreSolutions => {
88+
return true;
89+
}
90+
};
91+
92+
if !f(subst, !answers.peek_answer().is_no_more_solutions()) {
12793
return false;
12894
}
12995
}
130-
return true;
13196
}
13297

13398
/// True if all the tables on the stack starting from `depth` and
@@ -162,15 +127,22 @@ impl<C: Context> Forest<C> {
162127
self.tables[table].coinductive_goal
163128
})
164129
}
130+
}
165131

166-
/// Useful for testing.
167-
pub fn num_cached_answers_for_goal(
168-
&mut self,
169-
context: &impl ContextOps<C>,
170-
goal: &C::UCanonicalGoalInEnvironment,
171-
) -> usize {
172-
let table = self.get_or_create_table_for_ucanonical_goal(context, goal.clone());
173-
self.tables[table].num_cached_answers()
132+
#[derive(Debug)]
133+
pub enum SubstitutionResult<S> {
134+
Definite(S),
135+
Ambiguous(S),
136+
Floundered,
137+
}
138+
139+
impl<S: Display> Display for SubstitutionResult<S> {
140+
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
141+
match self {
142+
SubstitutionResult::Definite(subst) => write!(fmt, "{}", subst),
143+
SubstitutionResult::Ambiguous(subst) => write!(fmt, "Ambiguous({})", subst),
144+
SubstitutionResult::Floundered => write!(fmt, "Floundered"),
145+
}
174146
}
175147
}
176148

@@ -185,29 +157,25 @@ impl<'me, C: Context, CO: ContextOps<C>> AnswerStream<C> for ForestSolver<'me, C
185157
/// # Panics
186158
///
187159
/// Panics if a negative cycle was detected.
188-
fn peek_answer(&mut self) -> Option<CompleteAnswer<C>> {
160+
fn peek_answer(&mut self) -> AnswerResult<C> {
189161
loop {
190162
match self
191163
.forest
192164
.root_answer(self.context, self.table, self.answer)
193165
{
194166
Ok(answer) => {
195-
return Some(answer.clone());
167+
return AnswerResult::Answer(answer.clone());
196168
}
197169

198170
Err(RootSearchFail::InvalidAnswer) => {
199171
self.answer.increment();
200172
}
201173
Err(RootSearchFail::Floundered) => {
202-
let table_goal = &self.forest.tables[self.table].table_goal;
203-
return Some(CompleteAnswer {
204-
subst: self.context.identity_constrained_subst(table_goal),
205-
ambiguous: true,
206-
});
174+
return AnswerResult::Floundered;
207175
}
208176

209177
Err(RootSearchFail::NoMoreSolutions) => {
210-
return None;
178+
return AnswerResult::NoMoreSolutions;
211179
}
212180

213181
Err(RootSearchFail::QuantumExceeded) => {}
@@ -224,11 +192,10 @@ impl<'me, C: Context, CO: ContextOps<C>> AnswerStream<C> for ForestSolver<'me, C
224192
}
225193
}
226194

227-
fn next_answer(&mut self) -> Option<CompleteAnswer<C>> {
228-
self.peek_answer().map(|answer| {
229-
self.answer.increment();
230-
answer
231-
})
195+
fn next_answer(&mut self) -> AnswerResult<C> {
196+
let answer = self.peek_answer();
197+
self.answer.increment();
198+
answer
232199
}
233200

234201
fn any_future_answer(

chalk-engine/src/table.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,6 @@ impl<C: Context> Table<C> {
154154
self.answers.get(index.value)
155155
}
156156

157-
/// Useful for testing.
158-
pub fn num_cached_answers(&self) -> usize {
159-
self.answers.len()
160-
}
161-
162157
pub(super) fn next_answer_index(&self) -> AnswerIndex {
163158
AnswerIndex::from(self.answers.len())
164159
}

chalk-integration/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ edition = "2018"
1212
lalrpop-intern = "0.15.1"
1313
salsa = "0.10.0"
1414

15+
[dependencies.chalk-engine]
16+
version = "0.9.0"
17+
path = "../chalk-engine"
18+
1519
[dependencies.chalk-solve]
1620
version = "0.1.0"
1721
path = "../chalk-solve"

chalk-integration/src/db.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::error::ChalkError;
22
use crate::lowering::LowerGoal;
33
use crate::program::Program;
44
use crate::query::{Lowering, LoweringDatabase};
5+
use chalk_engine::forest::SubstitutionResult;
56
use chalk_ir::family::ChalkIr;
67
use chalk_ir::tls;
78
use chalk_ir::AssocTypeId;
@@ -70,7 +71,7 @@ impl ChalkDatabase {
7071
pub fn solve_multiple(
7172
&self,
7273
goal: &UCanonical<InEnvironment<Goal<ChalkIr>>>,
73-
f: impl FnMut(Canonical<ConstrainedSubst<ChalkIr>>, bool) -> bool,
74+
f: impl FnMut(SubstitutionResult<Canonical<ConstrainedSubst<ChalkIr>>>, bool) -> bool,
7475
) -> bool {
7576
let solver = self.solver();
7677
let solution = solver.lock().unwrap().solve_multiple(self, goal, f);

chalk-integration/src/query.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn coherence(
7676
) -> Result<BTreeMap<TraitId<ChalkIr>, Arc<SpecializationPriorities<ChalkIr>>>, ChalkError> {
7777
let program = db.program_ir()?;
7878

79-
tls::set_current_program(&program, || -> Result<_, ChalkError> {
79+
let priorities_map = tls::set_current_program(&program, || -> Result<_, ChalkError> {
8080
let priorities_map: Result<BTreeMap<_, _>, ChalkError> = program
8181
.trait_data
8282
.keys()
@@ -87,11 +87,10 @@ fn coherence(
8787
})
8888
.collect();
8989
let priorities_map = priorities_map?;
90-
91-
let () = db.orphan_check()?;
92-
9390
Ok(priorities_map)
94-
})
91+
});
92+
let () = db.orphan_check()?;
93+
priorities_map
9594
}
9695

9796
fn checked_program(db: &impl LoweringDatabase) -> Result<Arc<Program>, ChalkError> {

chalk-solve/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,3 @@ pub use solve::Guidance;
7575
pub use solve::Solution;
7676
pub use solve::Solver;
7777
pub use solve::SolverChoice;
78-
pub use solve::TestSolver;

0 commit comments

Comments
 (0)