-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add snapshotting to ObligationForest, take 2 #31175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @nikomatsakis (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
@soltanmm I've not read this code yet, but the ref-cell problem sounds annoying. Let me take a look. I know @jroesch spent quite some time wrestling with the best setup here and has been talking about rebasing a branch of his related to that. I think we can get things sorted out, we definitely want to think carefully a bit about where to draw the "refcell boundary". |
|
||
/// List of inversion actions that may be performed to undo mutations | ||
/// while in snapshots. | ||
undo_log: Vec<UndoAction<O>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern I used elsewhere is to have one vector and to push OpenSnapshot
events into it. This way you only need one vector (the vector is empty if no snapshots are active). Committing the base snapshot clears the vector (but leaves the state the same). Converting a non-base snapshot converts the OpenSnapshot
to CommittedSnapshot
. Reverting a snapshot pops things off of the undo vector. If you ever pop off an OpenSnapshot
(as opposed to a CommittedSnapshot
)...then something went wrong. You can see an example in src/librustc/middle/infer/region_inference.rs
.
OK, so, this code looks pretty good. I didn't dig into the precise details of each push-pop yet (or the precise patterns run by the tests), mostly because I wanted to raise one other question: to fix #31157, I think I want to add some "per-tree" state associated with the root (a However, it occurs to me now that the state I actually want in practice is a |
@nikomatsakis comments addressed. :-)
Just to clarify: when you say 'persistent', do you mean 'persistent enough to be able to backtrack to a parent' (as opposed to 'fully persistent')?
Something like a hook to push undoable actions (e.g. undoable via closures) into some other entity's log? Or something using the future |
I meany fully persistent, but of course the weaker property (which I would call "snapshottable") would suffice. It just makes the interface between OF and its clients a bit more complex. Let me do some experiments on my end, I'm still trying to figure out the best fix in any case. |
@nikomatsakis Within the Added safety could involve adding a flag to If it's required, ensuring that the number of obligations checked by the iterator is the same as the number that would've been checked in the current formulation of If it's required, ensuring that iterators created within an As best I can tell, this solves the problem of borrows by introducing an extra refcell in one location at argument position (new I'm probably going to attempt an implementation and see where it goes (might be this weekend; pants afire with other things), short of anyone saying that it's a no-go. |
@soltanmm Yes, that seems plausible. I checked out @jroesch's branch yesterday but haven't had time to play with it myself. It seems like we basically have two choices:
In general, I'd prefer to internalize, but that may indeed be very difficult. This is clearly what @jroesch has been doing, which is why I want to study his branch a bit, and maybe attempt some local ports, in order to see what is the most targeted diff we can achieve. I think in the end I might want to try it both ways, to see which works out the cleanest -- so I certainly have no objection to you giving that a try. It'd be an important data point. Sound good? |
Sounds good, but I have to wonder if it'd then be more prudent to stick with the approach in @jroesch's branch? Despite being a tad gung-ho I'm not attached to much beyond pushing my filed issues into history and leaving them there. :-) |
☔ The latest upstream changes (presumably #31349) made this pull request unmergeable. Please resolve the merge conflicts. |
Rebased (rewritten? sans |
☔ The latest upstream changes (presumably #31680) made this pull request unmergeable. Please resolve the merge conflicts. |
@soltanmm I think I'm going to close this PR for the time being-- but let's keep the branch around in case it turns out we do want to do this. :) |
Take 2 (3?) of adding snapshotting to
ObligationForest
, this time with a simpler undo-list approach (separate PR because the last one's commit deltas were nearly removed from history with these). Additionally adds snapshotting methods toFulfillmentContext
.Note: I tried adding the snapshotting of the
FulfillmentContext
to snapshotting calls inInferCtxt
and hitRefCell
borrow panics. Theaction
callback in theObligationForest::process_obligations
call attempts to borrow theFulfillmentContext
already in the middle of aselect_*
(specifically for a probe), and because we've already borrowed it in the earlier stack frame for callingselect_*
it falls on its face. If I understand the rest of the compiler correctly, the usual solution is to make everything aRefCell
. We could do that inObligationForest
and inFulfillmentContext
, but then that means every call toprocess_obligations
would need to make a copy of<self as ObligationForest>.nodes: Vec
to release all borrows surrounding the call toaction
inObligationForest::process_obligations
. Moreover, becauseaction
s may do more mutating things with theObligationForest
, the alternative of not making a copy via something unsafe could leave us with a dangling mutable reference if someaction
increases the size ofnodes
... which then leads to the alternative of using a stable vec (and other more complicated possibilities maintaining the required invariants). The rabbit hole goes down a ways...Playing with some notion of a
StackCell
(wc?) (something that explicitly allows one mutable borrow per snapshot level) could be fruitful, at least to bless the needed patterns of unsafe code for partially persistent structures as discouraged yet idiomatic, or perhaps much better finding a way of guaranteeing the invariants of snapshotting.r? @nikomatsakis
cc @jroesch (because I think you're doing something eventually with a snapshotting trait?)