2016-01-16 05:21:01 -05:00
|
|
|
The `ObligationForest` is a utility data structure used in trait
|
|
|
|
matching to track the set of outstanding obligations (those not yet
|
|
|
|
resolved to success or error). It also tracks the "backtrace" of each
|
|
|
|
pending obligation (why we are trying to figure this out in the first
|
|
|
|
place).
|
|
|
|
|
|
|
|
### External view
|
|
|
|
|
|
|
|
`ObligationForest` supports two main public operations (there are a
|
|
|
|
few others not discussed here):
|
|
|
|
|
2016-02-01 12:57:44 -05:00
|
|
|
1. Add a new root obligations (`push_tree`).
|
2016-01-16 05:21:01 -05:00
|
|
|
2. Process the pending obligations (`process_obligations`).
|
|
|
|
|
|
|
|
When a new obligation `N` is added, it becomes the root of an
|
2016-02-01 12:57:44 -05:00
|
|
|
obligation tree. This tree can also carry some per-tree state `T`,
|
|
|
|
which is given at the same time. This tree is a singleton to start, so
|
|
|
|
`N` is both the root and the only leaf. Each time the
|
|
|
|
`process_obligations` method is called, it will invoke its callback
|
|
|
|
with every pending obligation (so that will include `N`, the first
|
|
|
|
time). The callback also receives a (mutable) reference to the
|
|
|
|
per-tree state `T`. The callback should process the obligation `O`
|
|
|
|
that it is given and return one of three results:
|
2016-01-16 05:21:01 -05:00
|
|
|
|
|
|
|
- `Ok(None)` -> ambiguous result. Obligation was neither a success
|
|
|
|
nor a failure. It is assumed that further attempts to process the
|
|
|
|
obligation will yield the same result unless something in the
|
|
|
|
surrounding environment changes.
|
|
|
|
- `Ok(Some(C))` - the obligation was *shallowly successful*. The
|
|
|
|
vector `C` is a list of subobligations. The meaning of this is that
|
|
|
|
`O` was successful on the assumption that all the obligations in `C`
|
|
|
|
are also successful. Therefore, `O` is only considered a "true"
|
|
|
|
success if `C` is empty. Otherwise, `O` is put into a suspended
|
|
|
|
state and the obligations in `C` become the new pending
|
|
|
|
obligations. They will be processed the next time you call
|
|
|
|
`process_obligations`.
|
|
|
|
- `Err(E)` -> obligation failed with error `E`. We will collect this
|
|
|
|
error and return it from `process_obligations`, along with the
|
|
|
|
"backtrace" of obligations (that is, the list of obligations up to
|
|
|
|
and including the root of the failed obligation). No further
|
|
|
|
obligations from that same tree will be processed, since the tree is
|
|
|
|
now considered to be in error.
|
|
|
|
|
|
|
|
When the call to `process_obligations` completes, you get back an `Outcome`,
|
|
|
|
which includes three bits of information:
|
|
|
|
|
|
|
|
- `completed`: a list of obligations where processing was fully
|
|
|
|
completed without error (meaning that all transitive subobligations
|
|
|
|
have also been completed). So, for example, if the callback from
|
|
|
|
`process_obligations` returns `Ok(Some(C))` for some obligation `O`,
|
|
|
|
then `O` will be considered completed right away if `C` is the
|
|
|
|
empty vector. Otherwise it will only be considered completed once
|
|
|
|
all the obligations in `C` have been found completed.
|
|
|
|
- `errors`: a list of errors that occurred and associated backtraces
|
|
|
|
at the time of error, which can be used to give context to the user.
|
|
|
|
- `stalled`: if true, then none of the existing obligations were
|
|
|
|
*shallowly successful* (that is, no callback returned `Ok(Some(_))`).
|
|
|
|
This implies that all obligations were either errors or returned an
|
|
|
|
ambiguous result, which means that any further calls to
|
|
|
|
`process_obligations` would simply yield back further ambiguous
|
|
|
|
results. This is used by the `FulfillmentContext` to decide when it
|
|
|
|
has reached a steady state.
|
2016-02-16 10:36:47 -08:00
|
|
|
|
2016-01-16 05:21:01 -05:00
|
|
|
#### Snapshots
|
|
|
|
|
|
|
|
The `ObligationForest` supports a limited form of snapshots; see
|
|
|
|
`start_snapshot`; `commit_snapshot`; and `rollback_snapshot`. In
|
|
|
|
particular, you can use a snapshot to roll back new root
|
|
|
|
obligations. However, it is an error to attempt to
|
|
|
|
`process_obligations` during a snapshot.
|
|
|
|
|
|
|
|
### Implementation details
|
|
|
|
|
|
|
|
For the most part, comments specific to the implementation are in the
|
|
|
|
code. This file only contains a very high-level overview. Basically,
|
|
|
|
the forest is stored in a vector. Each element of the vector is a node
|
|
|
|
in some tree. Each node in the vector has the index of an (optional)
|
|
|
|
parent and (for convenience) its root (which may be itself). It also
|
|
|
|
has a current state, described by `NodeState`. After each
|
|
|
|
processing step, we compress the vector to remove completed and error
|
|
|
|
nodes, which aren't needed anymore.
|