Repository navigation
Track lifetimes of type parameters as lifetime projections - #195
Conversation
|
Ran the top 50 crates and the
|
zgrannan
left a comment
There was a problem hiding this comment.
this is a good start and is mostly on track I think. Let me know if any of comments need claraification
Body lifetime projections and call shapes now index into the generalized lifetime list, so places of opaque types get `RegionsIn(T)` projections. Outlives and liveness checks involving `RegionsIn` conservatively answer `true`. Work in progress: test files 025, 041, 069, 163 and 174 fail (all pass on main). Prusti does not build against this yet.
A constant argument has no place, so its post-call version cannot be observed. With `RegionsIn` projections, call shapes could select such an argument as an output and hit the `todo!()` in `expect_place`.
A borrow can only be stored in a value of type `X` if its region outlives some region in `X`. Regions hidden inside an opaque type are chosen by the caller and outlive the function body, so a region flows into `RegionsIn(X)` only if it outlives some universal region, or a region written in `X`. Previously such flows were always assumed, so e.g. the result of a call taking `&mut v` kept `v` blocked although the borrow checker allows it.
The maps from call-site lifetimes to the callee's identity signature are now keyed by generalized lifetimes, so `RegionsIn` at the call site is translated like a region and decided in the signature (e.g. `RegionsIn(I)` vs `RegionsIn(I::Item)` in `collect` becomes `RegionsIn(Self)` vs `RegionsIn(B)`). This fixes 041. Known regressions: 247 and 069 now fail. 247 hits the existing special case for a returned type parameter, which also fails on main for `take::<&'static u32>(&mut v)`.
A defined call's shape now requires both that the callee's signature allows a flow and that the borrow checker allows it at the call site. The signature alone has to account for every instantiation of the callee's type parameters, so e.g. `take::<&'static i32>(&mut v)` used to get an edge from the local `&mut` borrow into the `'static` result. Fixes 069, 247 and the new test 248 (which also fails on main).
`introduce_initial_borrows` iterated over the regions of an argument's type but created projections indexed by its generalized lifetimes. For an associated type with a lifetime argument (`<T as Tr<'a>>::Out`), the region `'a` is part of `RegionsIn(..)` and has no projection of its own, so the lookup failed and the PCG panicked.
Mirrors `Place::regions` and replaces direct calls to `extract_generalized_lifetimes` on a place's type.
Find the universal regions for 'static and the function body from the borrow checker facts, and use them instead of a hardcoded vid.
Keep the signature check in `outlives` as on main to keep the diff small, and add a test where the signature alone would allow cross edges.
6f3a30d to
2506577
Compare
|
I removed |
|
I worked in the feedback, thank you very much! Most importantly, I changed the way |
zgrannan
left a comment
There was a problem hiding this comment.
Looks good! I can merge it if you aren't able to for some reason
|
It doesn't seem like I can merge it |
This PR adds tracking of lifetimes of type parameters as lifetime projections. Take the following example:
Callers of
fmay substituteTwith a type containing regions, e.g.&'a mut i32. We would like to track these (unknown) regions through the generalized lifetimeRegionsIn(T)in the body off.The codebase already supports the right data structures with the
GeneralizedLifetimetype. This PR makes the following steps:KindinLifetimeProjectionand a couple other structs/enums toGeneralizedand thread this change through the codebaseGeneralizedLifetimeoutlives another one. This is the area where this PR deserves the most scrutinyAI Usage
Claude Code was used to help with creating the PR. All the code has been manually reviewed.
Note that I am not very familiar with the codebase.