Initial function call support in static analysis
This branch adds basic interprocedural analysis for pointer permissions. It propagates the "easy"/dataflow permissions, such as WRITE and OFFSET_ADD, across function calls and returns. For example, given fn f(x: *mut i32) { *x = 1; }, it will correctly infer that a call f(p) means that p must have WRITE permission, even if there is no explicit write in the calling function.
Handling of the UNIQUE permission is much more complex and is left for a separate branch. In short, at each function call, the borrow checker needs to know how the lifetimes of the various pointers in the arguments and return values are related so that it knows how the call affects borrows within the caller. For example, given fn g(x: *mut i32) -> *mut i32 { x }, it needs to know that the returned pointer borrows from the argument so it can properly remove UNIQUE for code like let y = g(x); let z = g(x); *y = 1;
Support for struct fields is also left for later, though that should be straightforward - pointers in field types simply get global PointerIds like function arguments do and participate in the dataflow analysis the same way.
I think I've addressed all the previous comments. I used the "resolve conversation" button liberally to help keep track of which threads I'd already handled - feel free to unresolve if there's more to discuss.
I've addressed the remaining comments. I added doc links only in the comments that were added or modified in this PR, to avoid polluting the diff with unrelated changes - the rest can be done in a separate PR if needed.