analyze: add NON_NULL rewrites
This branch implements rewriting of nullable pointers (those lacking PermissionSet::NON_NULL) to Option. This includes the following kinds of rewrites:
- Type annotations:
Option<&mut T>instead of&mut T - Casts between nullable and non-nullable via
p.unwrap()andSome(p). For most permissions, we implement only one direction because the other is invalid/nonsensical, but here we implement both in order to support "unsound" rewrites involving overriddenNON_NULLflags (as in #1088). - Borrows: when casting
p: Option<&mut T>, we usep.as_deref_mut().unwrap()instead ofp.unwrap()to avoid consuming the originalp. (In the code, this is called a "downgrade", since it allows borrowingOption<Box<T>>asOption<&T>and similar.) - Pointer projections on nullable pointers. Where
NON_NULLpointers would use&p[0], nullable ones usep.map(|ptr| &ptr[0]). Internally, this is represented similar toSome(&p.unwrap()[0]), but it's handled specially byrewrite::expr::convertto produce amapcall instead, which passes throughNonewithout a panic. unwrap()calls on derefs.*pis rewritten to*p.unwrap(), or to*p.as_deref().unwrap()if a downgrade/borrow is necessary to avoid movingp.ptr::null()and0 as *const _toNone, andp.is_null()top.is_none().
The new non_null_rewrites.rs test case passes, and the rewritten code compiles.
This might be easier to review commit-by-commit.
I realized while writing the PR that I never tested field projections (q = &(*p).field). I'll test it but will leave any fixes to a separate PR, since this one is already fairly complex.
Left a couple of comments. This is a large PR, it will take me a while to get through.
@ahomescu I've addressed all the previous feedback - any objections to merging this now?