Skip to content

Commit fba97af

Browse files
committed
Allow limiting search time using fuel
To use Chalk in an IDE environment, we need to be able to prevent it from taking too much time, even if that means getting a wrong or incomplete answer. This is done by specifying an amount of 'fuel'; each iteration of the top-level search loop takes one unit of fuel, and when it runs out, we return.
1 parent b92c153 commit fba97af

File tree

2 files changed

+42
-3
lines changed

2 files changed

+42
-3
lines changed

chalk-engine/src/forest.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ impl<C: Context> Forest<C> {
7676
&'f mut self,
7777
context: &'f impl ContextOps<C>,
7878
goal: &C::UCanonicalGoalInEnvironment,
79+
fuel: Option<usize>,
7980
) -> impl AnswerStream<C> + 'f {
8081
let table = self.get_or_create_table_for_ucanonical_goal(context, goal.clone());
8182
let answer = AnswerIndex::ZERO;
@@ -84,6 +85,7 @@ impl<C: Context> Forest<C> {
8485
context,
8586
table,
8687
answer,
88+
fuel,
8789
}
8890
}
8991

@@ -94,8 +96,9 @@ impl<C: Context> Forest<C> {
9496
&mut self,
9597
context: &impl ContextOps<C>,
9698
goal: &C::UCanonicalGoalInEnvironment,
99+
fuel: Option<usize>,
97100
) -> Option<C::Solution> {
98-
context.make_solution(C::canonical(&goal), self.iter_answers(context, goal))
101+
context.make_solution(C::canonical(&goal), self.iter_answers(context, goal, fuel))
99102
}
100103

101104
/// True if all the tables on the stack starting from `depth` and
@@ -147,6 +150,7 @@ struct ForestSolver<'me, C: Context + 'me, CO: ContextOps<C> + 'me> {
147150
context: &'me CO,
148151
table: TableIndex,
149152
answer: AnswerIndex,
153+
fuel: Option<usize>,
150154
}
151155

152156
impl<'me, C, CO> AnswerStream<C> for ForestSolver<'me, C, CO>
@@ -156,6 +160,13 @@ where
156160
{
157161
fn peek_answer(&mut self) -> Option<SimplifiedAnswer<C>> {
158162
loop {
163+
if let Some(fuel) = &mut self.fuel {
164+
if *fuel == 0 {
165+
// TODO: would it be better to return an ambiguous answer?
166+
return None;
167+
}
168+
*fuel -= 1;
169+
}
159170
match self
160171
.forest
161172
.ensure_root_answer(self.context, self.table, self.answer)

chalk-solve/src/solve.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,47 @@ impl Solver {
113113
/// each time you invoke `solve`, as otherwise the cached data may be
114114
/// invalid.
115115
/// - `goal` the goal to solve
116+
/// - `fuel` if `Some`, this limits how much time the solver spends before giving up.
116117
///
117118
/// # Returns
118119
///
119120
/// - `None` is the goal cannot be proven.
120121
/// - `Some(solution)` if we succeeded in finding *some* answers,
121122
/// although `solution` may reflect ambiguity and unknowns.
122-
pub fn solve(
123+
pub fn solve_with_fuel(
123124
&mut self,
124125
program: &dyn RustIrDatabase,
125126
goal: &UCanonical<InEnvironment<Goal>>,
127+
fuel: Option<usize>,
126128
) -> Option<Solution> {
127129
let ops = self.forest.context().ops(program);
128-
self.forest.solve(&ops, goal)
130+
self.forest.solve(&ops, goal, fuel)
131+
}
132+
133+
/// Attempts to solve the given goal, which must be in canonical
134+
/// form. Returns a unique solution (if one exists). This will do
135+
/// only as much work towards `goal` as it has to (and that work
136+
/// is cached for future attempts).
137+
///
138+
/// # Parameters
139+
///
140+
/// - `program` -- defines the program clauses in scope.
141+
/// - **Important:** You must supply the same set of program clauses
142+
/// each time you invoke `solve`, as otherwise the cached data may be
143+
/// invalid.
144+
/// - `goal` the goal to solve
145+
///
146+
/// # Returns
147+
///
148+
/// - `None` is the goal cannot be proven.
149+
/// - `Some(solution)` if we succeeded in finding *some* answers,
150+
/// although `solution` may reflect ambiguity and unknowns.
151+
pub fn solve(
152+
&mut self,
153+
program: &dyn RustIrDatabase,
154+
goal: &UCanonical<InEnvironment<Goal>>,
155+
) -> Option<Solution> {
156+
self.solve_with_fuel(program, goal, None)
129157
}
130158

131159
pub fn into_test(self) -> TestSolver {

0 commit comments

Comments
 (0)