# MIR Formality Roadmap ## "Spike"-based approach A "spike" is a sample program. The goal is to build out the end-to-end system for a given example. At each point, we do the "roughly" the minimum work needed to get the spike to work -- adding auxiliary tests around that functionality -- and then turn to the next spike to motivate follow-on work. We've chosen to use open soundness bugs as our spikes. ### Spike 0: Issue [#25860] [#25860]: https://github.com/rust-lang/rust/issues/25860 ```rust static UNIT: &'static &'static () = &&(); fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v } fn bad<'a, T>(x: &'a T) -> &'static T { let f: fn(_, &'a T) -> &'static T = foo; f(UNIT, x) } fn main() { let value = { let data = 22; bad(&data) }; let _ = *value; } ``` ```mermaid flowchart TD AutoTraits["Model auto traits"] Static["Model statics"] WfCheck AutoTraits --> Static ``` Steps to complete: * Add statics to decls * Model "auto traits" (`rust:Send`) * Add "ok" goals for statics: * Type of static must be well-formed * Type of static must implement `rust:Send` * Thing to test: static using out of scope names should be an error :) * Add enough MIR to model function bodies, static initializer * Add type-checker for MIR * Expect static, `foo`, and `main` to be well-typed * Expect `bad` not to be well-typed ### Spike 1: Issue [#25860] ## Yearly goal A complete model of safe Rust from "MIR backwards" Some subset of (maybe even all of) the following: * Type checker for MIR * Borrow check * Safety checks (e.g., detecting unsafe code) * Well-formedness checks for declarations * Operational semantics for safe Rust, probably similar to miri ## Milestones * Two questions I wanted answers to: * Interface between polonius and the trait checker * Reconcile the "everything is coinductive" rules with implied bounds * (needs an RFC) * Places where the design is not clear * Implied bounds * if you have `fn foo<'a, 'b>(x: &'a &'b u32)`, that function gets to assume that `'b: 'a` * if you have a where-clause like this `for<'a> u32: SomeTrait<'a>`, this is considered "legal" even if `u32` doesn't implement `SomeTrait` at all * Soundness questions around dyn* * Const generics / const-eval * what does "const evaluatable" mean and when can you use it? * broad question: * `const` bounds, etc etc * Full spike through the system of some sample program or various programs :) ## End to end example program ### Point ```rust struct Point { x: u32, y: u32 } fn main() { let p = Point { x: 22, y: 44 }; let x = p.x + p.y; } ``` Covers: * Struct declarations Sample MIR: ``` bb0: { StorageLive(_1); // scope 0 at test.rs:7:9: 7:10 _1 = Point { x: const 22_u32, y: const 44_u32 }; // scope 0 at test.rs:7:13: 7:35 FakeRead(ForLet(None), _1); // scope 0 at test.rs:7:9: 7:10 StorageLive(_2); // scope 1 at test.rs:8:9: 8:10 StorageLive(_3); // scope 1 at test.rs:8:13: 8:16 _3 = (_1.0: u32); // scope 1 at test.rs:8:13: 8:16 StorageLive(_4); // scope 1 at test.rs:8:19: 8:22 _4 = (_1.1: u32); // scope 1 at test.rs:8:19: 8:22 _5 = CheckedAdd(_3, _4); // scope 1 at test.rs:8:13: 8:22 assert(!move (_5.1: bool), "attempt to compute `{} + {}`, which would overflow", move _3, move _4) -> [success: bb1, unwind: bb2]; /> } bb1: { _2 = move (_5.0: u32); // scope 1 at test.rs:8:13: 8:22 StorageDead(_4); // scope 1 at test.rs:8:21: 8:22 StorageDead(_3); // scope 1 at test.rs:8:21: 8:22 FakeRead(ForLet(None), _2); // scope 1 at test.rs:8:9: 8:10 _0 = const (); // scope 0 at test.rs:6:11: 9:2 StorageDead(_2); // scope 1 at test.rs:9:1: 9:2 StorageDead(_1); // scope 0 at test.rs:9:1: 9:2 return; // scope 0 at test.rs:9:2: 9:2 } bb2 (cleanup): { resume; // scope 0 at test.rs:6:1: 9:2 } ``` ## Famous bugs ### 25860 ```rust static UNIT: &'static &'static () = &&(); fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v } fn bad<'a, T>(x: &'a T) -> &'static T { let f: fn(_, &'a T) -> &'static T = foo; f(UNIT, x) } fn main() { let value = { let data = 22; bad(&data) }; let _ = *value; } ``` This compiles today ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d0f42fb6c31d2a5c432946966765cf04)) but (a) should not and (b) will "go wrong" in the operational semantics. ## Complete roadmap for 25860 test * To type check the bodies of each function * Enough of the MIR type checker to do that * Determine the implied bounds and wf check for each function * (kind of have that today) * For statics, to check their types and the type of their initializer * also a MIR type check thing ## Further things we could check * The type of a static must be Send + Sync *