# Meeting 2022-12-07 * [ ] Meeting time (5 min) * We now pick Wednesday, but it's subject to changes next year. ## Field projection RFC: https://github.com/rust-lang/rfcs/pull/3318 Gary: Maybe we can add some other motivating use cases ```rust= #[repr(C)] struct DeviceRegisters { foo: u32, bar: u32, } ``` `IO<DeviceRegisters>` -> `Io<u32>` is a projection ```rust impl Io<u32> { fn read(&self) -> u32 { unsafe { bindings::readw(self.ptr) }; } } // use cases let io: &Io<DeviceRegisters> = unsafe { Io::map(/* IO address */) }; // Use projection here!!! io->foo.read(); ``` Benno: Would this be how Io would look like:? ```rust #[repr(transparent)] struct Io<T> { inner: UnsafeCell<T>, } ``` Gary: Yes Gary: Another application is seqlock. If you have write access, you still cannot have `&mut`. `SeqlockWriteGuard<Foo>`, e.g. `guard->foo.write(...)`. Alice: There are some other problems with using an Io type like this for memory that needs volatile access. You don't want to ever create references to it because references are marked with dereferenceable that lets LLVM insert spurious reads. - Gary: But `UnsafeCell` will prevent Rustc from making pointers `dereferencable`. Benno: I think if we could maybe use the `MaybeDangling` to fix this? Alice: I think it's easier to Gary: We can also do field projection with this: ```rust struct Io<'a, T>(*const T, PhantomData<&'a T>); ``` Boqun: ```rust= struct SmallStructA { a: u32, b: u32, } struct SmallStructB { a: u32, } // something defined in the spec struct BigStruct { f_a : SmallStructA f_b : SmallStructB } &BigStruct -> &SmallStructA pub fn do_something_forA(f_a: &Io<SmallStructA>) { .. } pub fn do_something_forB(f_a: &Io<SmallStructB>) { .. } let big_struct: &Io<BigStruct> = .. // this would be possible via field projection regardless of the implementation of `Io` // even with `Io<'_, BigStruct>` do_something_forA(big_struct->f_a) ``` * Gary: We can do this if we have field projections on values. Alice: dereferenceable was removed from `&UnsafeCell` for reasons entirely unrelated to volatile memory, which is why I am worried about this see e.g.: <https://github.com/rust-lang/unsafe-code-guidelines/issues/265#issuecomment-748481838> * Gary: Then we probably want the language team to either give us the guarantee, or give us a `VolatileCell`. * Alice: This is the fool-proof way to definitely do it correctly today: <https://users.rust-lang.org/t/how-to-make-an-access-volatile-without-std-library/85533/4> * Boqun: Does the 'VolatileCell' share the similar functionality as `core::arch::load/store`: https://github.com/rust-lang/unsafe-code-guidelines/issues/321 ? Gary: https://godbolt.org/z/vj8hxqcKT no optimized Gary: ```rust= struct Io<T>(PhantomData<T>); impl<T> Io<T> { fn read(&self) -> T { let ptr = self as *const _ as usize; std::ptr::read_volatile(ptr as *const T) } pub unsafe fn new<'a>(ptr: usize) -> &'a Self { &*(ptr as *const Io<T>) } pub unsafe fn new<'a>(ptr: &'a T) -> &'a Self { // Use https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.with_addr carefully in projection should be fine. &*(ptr as *const _ as usize as *const Self) } } ``` and use address only. # klint progress report * https://github.com/nbdd0121/klint * https://github.com/nbdd0121/linux/tree/klint Boqun: In theory, this could also detect `await` while holding a lock or preemption disabled, right? * Gary: I haven't looked into it actually. * Boqun: make `::poll()` a might_sleep function ```rust async fn async_bar() {} async fn async_foo(spinlock: &Spinlock) { let guard = spinlock.lock(); async_bar().await; } // translated to state machine (not actual code): fn async_foo(spinlock: &Spinlock) -> impl Future<Output = ()> { enum FooState<'a> { Start(&'a Spinlock), _1(&'a Spinlock, Guard<'this, ()>, FutureBar), Finished } impl<'a> Future for FooState<'a> { fn poll(self: Pin<&mut Self>, ctx: Context) -> Poll<()> { let mut state = unsafe { self.get_unchecked_mut() }; match state { FooState::Start(spinlock) => { let guard = spinlock.lock(); let bar = async_bar(); *state = _1(spinlock, guard, bar); = async_bar().poll(); // async_bar().await // never release the spinlock! problem! } FooState::_1 => { //... } } } } FooState::Start(spinloc) } ``` ``` error: cannot infer preemption count adjustment at this point --> foo.rs:71:16 | 71 | async_bar().await; | ^^^^^^ | note: preemption count adjustment is 1 after this --> foo.rs:70:17 | 70 | let guard = spinlock.lock(); | ^^^^^^^^^^^^^^^ note: while preemption count adjustment is 0 after this --> foo.rs:71:16 | 71 | async_bar().await; | ^^^^^^ ``` <details> <summary>MIR generated</summary> ```rust fn async_foo::{closure#0}(_1: std::pin::Pin<&mut [static generator@foo.rs:68:22: 73:2]>, _2: std::future::ResumeTy) -> std::ops::GeneratorState<(), ()> { debug _task_context => _19; // in scope 0 at foo.rs:68:22: 73:2 let mut _0: std::ops::GeneratorState<(), ()>; // return place in scope 0 at foo.rs:68:22: 73:2 let mut _3: &Spinlock; // in scope 0 at foo.rs:70:17: 70:32 let mut _4: impl std::future::Future<Output = ()>; // in scope 0 at foo.rs:71:16: 71:22 let mut _5: impl std::future::Future<Output = ()>; // in scope 0 at foo.rs:71:5: 71:16 let mut _6: std::task::Poll<()>; // in scope 0 at foo.rs:71:16: 71:22 let mut _7: std::pin::Pin<&mut impl std::future::Future<Output = ()>>; // in scope 0 at foo.rs:71:16: 71:22 let mut _8: &mut impl std::future::Future<Output = ()>; // in scope 0 at foo.rs:71:16: 71:22 let mut _9: &mut impl std::future::Future<Output = ()>; // in scope 0 at foo.rs:71:16: 71:22 let mut _10: &mut std::task::Context<'_>; // in scope 0 at foo.rs:71:5: 71:22 let mut _11: &mut std::task::Context<'_>; // in scope 0 at foo.rs:71:5: 71:22 let mut _12: std::future::ResumeTy; // in scope 0 at foo.rs:71:16: 71:22 let mut _13: isize; // in scope 0 at foo.rs:71:16: 71:22 let mut _15: std::future::ResumeTy; // in scope 0 at foo.rs:71:16: 71:22 let mut _16: (); // in scope 0 at foo.rs:71:16: 71:22 let _17: (); // in scope 0 at foo.rs:72:5: 72:12 let mut _18: (); // in scope 0 at foo.rs:68:22: 73:2 let mut _19: std::future::ResumeTy; // in scope 0 at foo.rs:68:22: 73:2 let mut _20: u32; // in scope 0 at foo.rs:68:22: 73:2 let mut _21: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _22: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _23: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _24: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _25: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _26: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _27: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _28: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _29: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _30: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 let mut _31: &mut [static generator@foo.rs:68:22: 73:2]; // in scope 0 at foo.rs:68:22: 73:2 scope 1 { debug spinlock => (((*(_1.0: &mut [static generator@foo.rs:68:22: 73:2])) as variant#3).0: Spinlock); // in scope 1 at foo.rs:69:9: 69:17 scope 2 { debug guard => (((*(_1.0: &mut [static generator@foo.rs:68:22: 73:2])) as variant#3).1: Guard); // in scope 2 at foo.rs:70:9: 70:14 scope 3 { debug __awaitee => (((*(_1.0: &mut [static generator@foo.rs:68:22: 73:2])) as variant#3).2: impl std::future::Future<Output = ()>); // in scope 3 at foo.rs:71:16: 71:22 let _14: (); // in scope 3 at foo.rs:71:5: 71:22 scope 4 { } scope 5 { debug result => _14; // in scope 5 at foo.rs:71:5: 71:22 } } } } bb0: { _21 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 0 at foo.rs:68:22: 73:2 _20 = discriminant((*_21)); // scope 0 at foo.rs:68:22: 73:2 switchInt(move _20) -> [0_u32: bb1, 1_u32: bb18, 2_u32: bb17, 3_u32: bb16, otherwise: bb19]; // scope 0 at foo.rs:68:22: 73:2 } bb1: { _19 = move _2; // scope 0 at foo.rs:68:22: 73:2 _22 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 0 at foo.rs:69:20: 69:28 Deinit((((*_22) as variant#3).0: Spinlock)); // scope 0 at foo.rs:69:20: 69:28 _23 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 1 at foo.rs:70:17: 70:32 _3 = &(((*_23) as variant#3).0: Spinlock); // scope 1 at foo.rs:70:17: 70:32 _24 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 1 at foo.rs:70:17: 70:32 (((*_24) as variant#3).1: Guard) = Spinlock::lock(move _3) -> [return: bb2, unwind: bb15]; // scope 1 at foo.rs:70:17: 70:32 // mir::Constant // + span: foo.rs:70:26: 70:30 // + literal: Const { ty: for<'a> fn(&'a Spinlock) -> Guard {Spinlock::lock}, val: Value(<ZST>) } } bb2: { _5 = async_bar() -> [return: bb3, unwind: bb14]; // scope 2 at foo.rs:71:5: 71:16 // mir::Constant // + span: foo.rs:71:5: 71:14 // + literal: Const { ty: fn() -> impl std::future::Future<Output = ()> {async_bar}, val: Value(<ZST>) } } bb3: { _4 = <impl std::future::Future<Output = ()> as std::future::IntoFuture>::into_future(move _5) -> [return: bb4, unwind: bb14]; // scope 2 at foo.rs:71:16: 71:22 // mir::Constant // + span: foo.rs:71:16: 71:22 // + literal: Const { ty: fn(impl std::future::Future<Output = ()>) -> <impl std::future::Future<Output = ()> as std::future::IntoFuture>::IntoFuture {<impl std::future::Future<Output = ()> as std::future::IntoFuture>::into_future}, val: Value(<ZST>) } } bb4: { _25 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 2 at foo.rs:71:16: 71:22 (((*_25) as variant#3).2: impl std::future::Future<Output = ()>) = move _4; // scope 2 at foo.rs:71:16: 71:22 goto -> bb5; // scope 3 at foo.rs:71:16: 71:22 } bb5: { _26 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 4 at foo.rs:71:16: 71:22 _9 = &mut (((*_26) as variant#3).2: impl std::future::Future<Output = ()>); // scope 4 at foo.rs:71:16: 71:22 _8 = &mut (*_9); // scope 4 at foo.rs:71:16: 71:22 _7 = std::pin::Pin::<&mut impl std::future::Future<Output = ()>>::new_unchecked(move _8) -> [return: bb6, unwind: bb14]; // scope 4 at foo.rs:71:16: 71:22 // mir::Constant // + span: foo.rs:71:16: 71:22 // + literal: Const { ty: unsafe fn(&mut impl std::future::Future<Output = ()>) -> std::pin::Pin<&mut impl std::future::Future<Output = ()>> {std::pin::Pin::<&mut impl std::future::Future<Output = ()>>::new_unchecked}, val: Value(<ZST>) } } bb6: { _12 = _19; // scope 4 at foo.rs:71:16: 71:22 _11 = std::future::get_context::<'_, '_>(move _12) -> [return: bb7, unwind: bb14]; // scope 4 at foo.rs:71:5: 71:22 // mir::Constant // + span: foo.rs:71:5: 71:22 // + literal: Const { ty: unsafe fn(std::future::ResumeTy) -> &mut std::task::Context<'_> {std::future::get_context::<'_, '_>}, val: Value(<ZST>) } } bb7: { _10 = &mut (*_11); // scope 4 at foo.rs:71:5: 71:22 _6 = <impl std::future::Future<Output = ()> as std::future::Future>::poll(move _7, move _10) -> [return: bb8, unwind: bb14]; // scope 4 at foo.rs:71:16: 71:22 // mir::Constant // + span: foo.rs:71:16: 71:22 // + literal: Const { ty: for<'a, 'b, 'c> fn(std::pin::Pin<&'a mut impl std::future::Future<Output = ()>>, &'b mut std::task::Context<'c>) -> std::task::Poll<<impl std::future::Future<Output = ()> as std::future::Future>::Output> {<impl std::future::Future<Output = ()> as std::future::Future>::poll}, val: Value(<ZST>) } } bb8: { _13 = discriminant(_6); // scope 3 at foo.rs:71:16: 71:22 switchInt(move _13) -> [0_isize: bb11, 1_isize: bb9, otherwise: bb10]; // scope 3 at foo.rs:71:16: 71:22 } bb9: { Deinit(_16); // scope 3 at foo.rs:71:16: 71:22 Deinit(_0); // scope 3 at foo.rs:71:16: 71:22 ((_0 as Yielded).0: ()) = move _16; // scope 3 at foo.rs:71:16: 71:22 discriminant(_0) = 0; // scope 3 at foo.rs:71:16: 71:22 _27 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 3 at foo.rs:71:16: 71:22 discriminant((*_27)) = 3; // scope 3 at foo.rs:71:16: 71:22 return; // scope 3 at foo.rs:71:16: 71:22 } bb10: { unreachable; // scope 3 at foo.rs:71:16: 71:22 } bb11: { _14 = ((_6 as Ready).0: ()); // scope 3 at foo.rs:71:5: 71:22 _17 = sleep() -> [return: bb12, unwind: bb14]; // scope 2 at foo.rs:72:5: 72:12 // mir::Constant // + span: foo.rs:72:5: 72:10 // + literal: Const { ty: fn() {sleep}, val: Value(<ZST>) } } bb12: { _18 = const (); // scope 0 at foo.rs:68:22: 73:2 _28 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 1 at foo.rs:73:1: 73:2 drop((((*_28) as variant#3).1: Guard)) -> [return: bb13, unwind: bb15]; // scope 1 at foo.rs:73:1: 73:2 } bb13: { Deinit(_0); // scope 0 at foo.rs:73:2: 73:2 ((_0 as Complete).0: ()) = move _18; // scope 0 at foo.rs:73:2: 73:2 discriminant(_0) = 1; // scope 0 at foo.rs:73:2: 73:2 _29 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 0 at foo.rs:73:2: 73:2 discriminant((*_29)) = 1; // scope 0 at foo.rs:73:2: 73:2 return; // scope 0 at foo.rs:73:2: 73:2 } bb14 (cleanup): { _30 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 1 at foo.rs:73:1: 73:2 drop((((*_30) as variant#3).1: Guard)) -> bb15; // scope 1 at foo.rs:73:1: 73:2 } bb15 (cleanup): { _31 = deref_copy (_1.0: &mut [static generator@foo.rs:68:22: 73:2]); // scope 0 at foo.rs:68:22: 73:2 discriminant((*_31)) = 2; // scope 0 at foo.rs:68:22: 73:2 resume; // scope 0 at foo.rs:68:22: 73:2 } bb16: { _15 = move _2; // scope 0 at foo.rs:68:22: 73:2 _19 = move _15; // scope 3 at foo.rs:71:16: 71:22 goto -> bb5; // scope 3 at foo.rs:71:16: 71:22 } bb17: { assert(const false, "`async fn` resumed after panicking") -> bb17; // scope 0 at foo.rs:68:22: 73:2 } bb18: { assert(const false, "`async fn` resumed after completion") -> bb18; // scope 0 at foo.rs:68:22: 73:2 } bb19: { unreachable; // scope 0 at foo.rs:68:22: 73:2 } } ``` </details> # Miguel's report LLVM CFI updates: https://reviews.llvm.org/D139395