Skip to content

Commit d42a5bf

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 201775c commit d42a5bf

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
@@ -77,6 +77,7 @@ impl<C: Context> Forest<C> {
7777
&'f mut self,
7878
context: &'f impl ContextOps<C>,
7979
goal: &C::UCanonicalGoalInEnvironment,
80+
fuel: Option<usize>,
8081
) -> impl AnswerStream<C> + 'f {
8182
let table = self.get_or_create_table_for_ucanonical_goal(context, goal.clone());
8283
let answer = AnswerIndex::ZERO;
@@ -85,6 +86,7 @@ impl<C: Context> Forest<C> {
8586
context,
8687
table,
8788
answer,
89+
fuel,
8890
}
8991
}
9092

@@ -95,8 +97,9 @@ impl<C: Context> Forest<C> {
9597
&mut self,
9698
context: &impl ContextOps<C>,
9799
goal: &C::UCanonicalGoalInEnvironment,
100+
fuel: Option<usize>,
98101
) -> Option<C::Solution> {
99-
context.make_solution(C::canonical(&goal), self.iter_answers(context, goal))
102+
context.make_solution(C::canonical(&goal), self.iter_answers(context, goal, fuel))
100103
}
101104

102105
/// True if all the tables on the stack starting from `depth` and
@@ -148,6 +151,7 @@ struct ForestSolver<'me, C: Context + 'me, CO: ContextOps<C> + 'me> {
148151
context: &'me CO,
149152
table: TableIndex,
150153
answer: AnswerIndex,
154+
fuel: Option<usize>,
151155
}
152156

153157
impl<'me, C, CO> AnswerStream<C> for ForestSolver<'me, C, CO>
@@ -157,6 +161,13 @@ where
157161
{
158162
fn peek_answer(&mut self) -> Option<SimplifiedAnswer<C>> {
159163
loop {
164+
if let Some(fuel) = &mut self.fuel {
165+
if *fuel == 0 {
166+
// TODO: would it be better to return an ambiguous answer?
167+
return None;
168+
}
169+
*fuel -= 1;
170+
}
160171
match self
161172
.forest
162173
.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)