Done so far
Apr 9th 2019:
Exploratory coding branch (on top of recent master) can be found
- Use the cmt_ machinery to for all(?) consumes/borrows, where each (upvar, path) is associated with an UpvarCapture.
- This required Rvalue(ty::Region<'tcx>) to Rvalue(ty::Region<'tcx>, Option<Box<cmt_<'tcx>>>), to be able to thread the cmt_s.
- When a function carries the rustc_dump_closure_captures attribute, for all closures defined in it.
Apr 16th 2019:
- exploratory: and to it.
- start writing tests (most ), some inline below
- fix a couple of revealed by the tests
- the UpvarCapture decisions for children paths to their ancestors
- Implement “would a narrower definition cause us to move out of drop” check, but see below
Apr 23rd 2019
- Explicitly ThreadLocal, StaticItem and Local.
Apr 23rd 2019 (Nicholas Matsakis):
- Questions to ponder:
- Presently, we are using the ExprUseVisitor to do our closure upvar analysis; it constructs cmts from moved paths etc. It gives us a bit more information than we presently need — if we were to do design an interface from scratch though, how would it differ?
- It seems like it would wind up giving roughly the same information that we give now.
- I think in my ideal world we would use HAIR as the basis for this translation, but HAIR presently makes the captures of upvars quite explicit.
- Why do we extend Rvalue with additional information? When we have a cmt that borrows an rvalue, that implies that we believe a temporary was created. The extra information is tracking the origin of that value. Perhaps the creation of that temporary itself needs to be “recorded”? Something feels a bit fishy here, maybe?
- Can we settle on the behavior? What are the edge cases?
- e.g., I imagine that if we have &mut x.y.z and &x.y, we would propagate &mut x.y only, and rewrite the former to &mut (*upvar).z and the latter to &*upvar.
- But in what cases do we “freeze” propagation etc.
- Probably I should re-skim the RFC
- Example of a tricky scenario: x: (&(u32,), ). Consider the path x.0.0 — if we are captured &x.0.0 precisely, we would then be able to mutate x and the closure wouldn’t notice ():
let mut x = (&(22,),);
let p = &x.0.0; // imagine this is part of constructing a closure
x.0 = &(44,);
- This seems like something we don’t want — , it does not compile today.
A vague plan takes shape:
- Exhaustive test cases
- what is the “matrix” we can use? we want to be sure that we have:
- closures capturing paths that traverse
- structs with fields that have drop (but not the path itself)
- moving out of structs that impl drop
- Rc (only overloads Deref, sort of the easy case I suspect)
- something that overloads DerefMut but is not Box
- crossed by a main function that
- also replace cmt with something simpler, more like MIR place, let’s call it CapturePath
- this refactoring can be done independently as it should not change existing behavior
- Note though that, in an ideal world, we would be able to unit test the CapturePath interaction — so that e.g. we can unit test what happens as we traverse the
- it will also , mapping the HIR expressions to the upvars that replace them