1 //! An alternative solver based around the SLG algorithm, which
2 //! implements the well-formed semantics. This algorithm is very
3 //! closed based on the description found in the following paper,
4 //! which I will refer to in the comments as EWFS:
6 //! > Efficient Top-Down Computation of Queries Under the Well-formed Semantics
7 //! > (Chen, Swift, and Warren; Journal of Logic Programming '95)
9 //! However, to understand that paper, I would recommend first
10 //! starting with the following paper, which I will refer to in the
13 //! > A New Formulation of Tabled resolution With Delay
14 //! > (Swift; EPIA '99)
16 //! In addition, I incorporated extensions from the following papers,
17 //! which I will refer to as SA and RR respectively, that
18 //! describes how to do introduce approximation when processing
19 //! subgoals and so forth:
21 //! > Terminating Evaluation of Logic Programs with Finite Three-Valued Models
22 //! > Riguzzi and Swift; ACM Transactions on Computational Logic 2013
23 //! > (Introduces "subgoal abstraction", hence the name SA)
25 //! > Radial Restraint
26 //! > Grosof and Swift; 2013
28 //! Another useful paper that gives a kind of high-level overview of
29 //! concepts at play is the following, which I will refer to as XSB:
31 //! > XSB: Extending Prolog with Tabled Logic Programming
32 //! > (Swift and Warren; Theory and Practice of Logic Programming '10)
34 //! While this code is adapted from the algorithms described in those
35 //! papers, it is not the same. For one thing, the approaches there
36 //! had to be extended to our context, and in particular to coping
37 //! with hereditary harrop predicates and our version of unification
38 //! (which produces subgoals). I believe those to be largely faithful
39 //! extensions. However, there are some other places where I
40 //! intentionally dieverged from the semantics as described in the
41 //! papers -- e.g. by more aggressively approximating -- which I
42 //! marked them with a comment DIVERGENCE. Those places may want to be
43 //! evaluated in the future.
45 //! Glossary of other terms:
47 //! - WAM: Warren abstract machine, an efficient way to evaluate Prolog programs.
48 //! See <http://wambook.sourceforge.net/>.
49 //! - HH: Hereditary harrop predicates. What Chalk deals in.
50 //! Popularized by Lambda Prolog.
52 #![feature(in_band_lifetimes)]
53 #![feature(step_trait)]
54 #![feature(non_modrs_mods)]
57 extern crate chalk_macros
;
59 #[cfg(feature = "stack_protection")]
62 extern crate rustc_hash
;
65 use rustc_hash
::FxHashSet
;
82 pub struct TableIndex
{ // FIXME: pub b/c Fold
87 /// The StackIndex identifies the position of a table's goal in the
88 /// stack of goals that are actively being processed. Note that once a
89 /// table is completely evaluated, it may be popped from the stack,
90 /// and hence no longer have a stack index.
97 /// The `DepthFirstNumber` (DFN) is a sequential number assigned to
98 /// each goal when it is first encountered. The naming (taken from
99 /// EWFS) refers to the idea that this number tracks the index of when
100 /// we encounter the goal during a depth-first traversal of the proof
102 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
103 struct DepthFirstNumber
{
107 /// The paper describes these as `A :- D | G`.
108 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
109 pub struct ExClause
<C
: Context
> {
110 /// The substitution which, applied to the goal of our table,
112 pub subst
: C
::Substitution
,
114 /// Delayed literals: things that we depend on negatively,
115 /// but which have not yet been fully evaluated.
116 pub delayed_literals
: Vec
<DelayedLiteral
<C
>>,
118 /// Region constraints we have accumulated.
119 pub constraints
: Vec
<C
::RegionConstraint
>,
121 /// Subgoals: literals that must be proven
122 pub subgoals
: Vec
<Literal
<C
>>,
125 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
126 struct SimplifiedAnswers
<C
: Context
> {
127 answers
: Vec
<SimplifiedAnswer
<C
>>,
130 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
131 pub struct SimplifiedAnswer
<C
: Context
> {
132 /// A fully instantiated version of the goal for which the query
133 /// is true (including region constraints).
134 pub subst
: C
::CanonicalConstrainedSubst
,
136 /// If this flag is set, then the answer could be neither proven
137 /// nor disproven. In general, the existence of a non-empty set of
138 /// delayed literals simply means the answer's status is UNKNOWN,
139 /// either because the size of the answer exceeded `max_size` or
140 /// because of a negative loop (e.g., `P :- not { P }`).
145 struct DelayedLiteralSets
<C
: Context
>(InnerDelayedLiteralSets
<C
>);
147 #[derive(Clone, Debug, PartialEq, Eq)]
148 enum InnerDelayedLiteralSets
<C
: Context
> {
149 /// Corresponds to a single, empty set.
152 /// Some (non-zero) number of non-empty sets.
153 /// Must be a set of sets, but HashSets are not Hash so we manually ensure uniqueness.
154 Some(Vec
<DelayedLiteralSet
<C
>>),
157 /// A set of delayed literals.
159 /// (One might expect delayed literals to always be ground, since
160 /// non-ground negative literals result in flounded
161 /// executions. However, due to the approximations introduced via RR
162 /// to ensure termination, it *is* in fact possible for delayed goals
163 /// to contain free variables. For example, what could happen is that
164 /// we get back an approximated answer with `Goal::CannotProve` as a
165 /// delayed literal, which in turn forces its subgoal to be delayed,
166 /// and so forth. Therefore, we store canonicalized goals.)
167 #[derive(Clone, Debug, Default)]
168 struct DelayedLiteralSet
<C
: Context
> {
169 delayed_literals
: FxHashSet
<DelayedLiteral
<C
>>,
172 #[derive(Clone, Debug)]
173 pub enum DelayedLiteral
<C
: Context
> {
174 /// Something which can never be proven nor disproven. Inserted
175 /// when truncation triggers; doesn't arise normally.
178 /// We are blocked on a negative literal `~G`, where `G` is the
179 /// goal of the given table. Because negative goals must always be
180 /// ground, we don't need any other information.
181 Negative(TableIndex
),
183 /// We are blocked on a positive literal `Li`; we found a
184 /// **conditional** answer (the `CanonicalConstrainedSubst`) within the
185 /// given table, but we have to come back later and see whether
186 /// that answer turns out to be true.
187 Positive(TableIndex
, C
::CanonicalConstrainedSubst
),
190 /// Either `A` or `~A`, where `A` is a `Env |- Goal`.
191 #[derive(Clone, Debug)]
192 pub enum Literal
<C
: Context
> { // FIXME: pub b/c fold
193 Positive(C
::GoalInEnvironment
),
194 Negative(C
::GoalInEnvironment
),
197 /// The `Minimums` structure is used to track the dependencies between
198 /// some item E on the evaluation stack. In particular, it tracks
199 /// cases where the success of E depends (or may depend) on items
200 /// deeper in the stack than E (i.e., with lower DFNs).
202 /// `positive` tracks the lowest index on the stack to which we had a
203 /// POSITIVE dependency (e.g. `foo(X) :- bar(X)`) -- meaning that in
204 /// order for E to succeed, the dependency must succeed. It is
205 /// initialized with the index of the predicate on the stack. So
206 /// imagine we have a stack like this:
208 /// // 0 foo(X) <-- bottom of stack
210 /// // 2 baz(X) <-- top of stack
212 /// In this case, `positive` would be initially 0, 1, and 2 for `foo`,
213 /// `bar`, and `baz` respectively. This reflects the fact that the
214 /// answers for `foo(X)` depend on the answers for `foo(X)`. =)
216 /// Now imagine that we had a clause `baz(X) :- foo(X)`, inducing a
217 /// cycle. In this case, we would update `positive` for `baz(X)` to be
218 /// 0, reflecting the fact that its answers depend on the answers for
219 /// `foo(X)`. Similarly, the minimum for `bar` would (eventually) be
220 /// updated, since it too transitively depends on `foo`. `foo` is
223 /// `negative` tracks the lowest index on the stack to which we had a
224 /// NEGATIVE dependency (e.g., `foo(X) :- not { bar(X) }`) -- meaning
225 /// that for E to succeed, the dependency must fail. This is initially
226 /// `usize::MAX`, reflecting the fact that the answers for `foo(X)` do
227 /// not depend on `not(foo(X))`. When negative cycles are encountered,
228 /// however, this value must be updated.
229 #[derive(Copy, Clone, Debug)]
231 positive
: DepthFirstNumber
,
232 negative
: DepthFirstNumber
,
235 impl<C
: Context
> DelayedLiteralSets
<C
> {
236 fn singleton(set
: DelayedLiteralSet
<C
>) -> Self {
238 DelayedLiteralSets(InnerDelayedLiteralSets
::None
)
240 DelayedLiteralSets(InnerDelayedLiteralSets
::Some(vec
![set
]))
244 /// Inserts the set if it is minimal in the family.
245 /// Returns true iff the set was inserted.
246 fn insert_if_minimal(&mut self, set
: &DelayedLiteralSet
<C
>) -> bool
{
248 // The empty set is always minimal.
249 InnerDelayedLiteralSets
::None
=> false,
250 // Are we inserting an empty set?
251 InnerDelayedLiteralSets
::Some(_
) if set
.is_empty() => {
252 self.0 = InnerDelayedLiteralSets
::None
;
255 InnerDelayedLiteralSets
::Some(ref mut sets
) => {
256 // Look for a subset.
257 if sets
.iter().any(|set
| set
.is_subset(&set
)) {
260 // No subset therefore `set` is minimal, discard supersets and insert.
261 sets
.retain(|set
| !set
.is_subset(set
));
262 sets
.push(set
.clone());
270 impl<C
: Context
> DelayedLiteralSet
<C
> {
271 fn is_empty(&self) -> bool
{
272 self.delayed_literals
.is_empty()
275 fn is_subset(&self, other
: &DelayedLiteralSet
<C
>) -> bool
{
276 self.delayed_literals
278 .all(|elem
| other
.delayed_literals
.contains(elem
))
283 const MAX
: Minimums
= Minimums
{
284 positive
: DepthFirstNumber
::MAX
,
285 negative
: DepthFirstNumber
::MAX
,
288 /// Update our fields to be the minimum of our current value
289 /// and the values from other.
290 fn take_minimums(&mut self, other
: &Minimums
) {
291 self.positive
= min(self.positive
, other
.positive
);
292 self.negative
= min(self.negative
, other
.negative
);
295 fn minimum_of_pos_and_neg(&self) -> DepthFirstNumber
{
296 min(self.positive
, self.negative
)
300 impl DepthFirstNumber
{
301 const MIN
: DepthFirstNumber
= DepthFirstNumber { value: 0 }
;
302 const MAX
: DepthFirstNumber
= DepthFirstNumber
{
303 value
: ::std
::u64::MAX
,
306 fn next(&mut self) -> DepthFirstNumber
{
307 let value
= self.value
;
308 assert
!(value
< ::std
::u64::MAX
);
310 DepthFirstNumber { value }
314 /// Because we recurse so deeply, we rely on stacker to
315 /// avoid overflowing the stack.
316 #[cfg(feature = "stack_protection")]
317 fn maybe_grow_stack
<F
, R
>(op
: F
) -> R
321 // These numbers are somewhat randomly chosen to make tests work
322 // well enough on my system. In particular, because we only test
323 // for growing the stack in `new_clause`, a red zone of 32K was
324 // insufficient to prevent stack overflow. - nikomatsakis
325 stacker
::maybe_grow(256 * 1024, 2 * 1024 * 1024, op
)
328 #[cfg(not(feature = "stack_protection"))]
329 fn maybe_grow_stack
<F
, R
>(op
: F
) -> R