The const_raw_ptr_deref feature allows dereferencing raw pointers inside constants.
This can already be emulated via union transmuting raw pointers to references and referencing the references. This feature gate helps us experiment with the semantics of actually dereferencing normally.
Blocked on https://github.com/rust-lang/const-eval/issues/14.
What does this feature actually do?
To my mind the primary use case for this is going to be static references to memory-mapped I/O registers in embedded development; however, the obvious thing
static REG_DEBUG_ENABLE: &'static u16 = unsafe { &*(0x4fff780 as *mut u16) };
gets me the error
this static likely exhibits undefined behavior
type validation failed: encountered integer pointer in non-ZST reference
note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
which suggests that it's constructing 0x4fff780 as *mut u16 on the host machine at compile-time, dereferencing it and substituting what it found so that dereferencing REG_DEBUG_ENABLE on the target machine at runtime produces the same value, when I just wanted a typecast.
which suggests that it's constructing 0x4fff780 as *mut u16 on the host machine at compile-time,
Not quite. That happens transparently. The error comes from the sanity checks on the value of constants and statics.
This feature is for when you converted a safe reference to a raw pointer, allowing you to turn it back into a safe reference.
What you are trying to do is simply not allowed (yet?).
Side-note: Additionally it seems dangerous to create references to registers without using volatile accessors. A &'static u16 guarantees (to best of my knowledge) that the value pointed at will not change at all during the entire runtime of your program.
Yes, &'static u16 is wrong for MMIO, you'd want something like &'static VolatileCell<u16> (for a suitable definition of VolatileCell), though that doesn't change anything with respect to the pointer casting that needs to happen.
I wasn't being very careful when throwing out an example. Pretend I used &mut instead and waved my hands about volatile accesses.
(Sidenote: I'm surprised VolatileCell isn't provided in the standard library.)
&'static mut T is definitely UB here.
And once you are at the VolatileCell level, you might as well make that a ZST with a phantomdata on your concrete type and everything will work out nicely.
Okay, now I'm lost. It sounds like you just told me it's undefined behaviour to access system memory unless I allocate something on the stack first.
If you had a way to create &'static mut T from an integer safely, then you could do that twice and thus end up with two &mut T to the same address. This is UB in Rust.
What I was suggesting was that you'd use
#![feature(const_raw_ptr_deref)]
const FOO: &'static VolatileCell<u32> = unsafe { &*(0x4fff780 as *const VolatileCell<u32>) };
struct VolatileCell<T: Copy> {
marker: std::marker::PhantomData<T>,
}
impl<T: Copy> VolatileCell<T> {
fn read(&self) -> T {
unsafe {
std::ptr::read_volatile(self as *const _ as *const T)
}
}
}
to manage the access to device addresses
FWIW, the use case I have for this is to create #[repr(transparent)] DSTs as consts:
#[repr(transparent)]
pub struct CStr(str);
impl CStr {
/// Creates a new CStr from a str without performing any additional checks. `data` _must_ end
/// with a NUL byte, and should only have only a single NUL byte, or the string will be
/// truncated.
pub const unsafe fn new_unchecked(data: &str) -> &CStr {
&*(data as *const str as *const CStr)
}
}
Related: every function in std::ptr should be const. AFAICT there's no reason for them not to be.
Most helpful comment
FWIW, the use case I have for this is to create
#[repr(transparent)]DSTs as consts: