In between other work, I've been forced to think about memory quite a bit lately. In particular, things like memory mapped I/O, memory mapped files, NUMA architectures, and other kinds of arcane and twisty things-that-pretend-to-be-ordinary-memory-but-aren't. This got me thinking about how we could tell rust that certain address ranges have certain attributes, and I think I may have an idea how to do so, but it would mean that we'd need to create a new, probably magical type that the compiler is fully aware of, and therefore knows how to treat specially. Here's the rough outline of the type in pseduo-rust:
use std::ops::Range;
#[non_exhaustive]
pub enum Attribute{
NoReadNoWrite,
ReadOnly,
WriteOnly,
ReadWrite,
MemoryMappedIO,
// I have no idea what other stuff could be added, but
// I'm sure it'll be a lot!
}
pub struct AddressRangeAttributes {
range: Range<usize>,
attributes: Vec<Attribute> // Maybe a set would be better?
}
So far, this is pretty boring (and likely both incomplete and not fine-grained enough, but I digress), but the magic is that there is a side-effect when you create an instance; the compiler knows that for the lifetime of that instance, that address range has those attributes. So a function that creates memory-mapped IO could do something similar to the following:
pub struct MemoryMappedFile {
_memory_attributes: AddressRangeAttributes,
// Whatever else is needed by this type.
}
impl MemoryMappedFile {
pub new() -> Result<MemoryMappedFile, Error> {
Ok(MemoryMappedFile{
_memory_attributes: AddressRangeAttributes::new(...)?,
// Etc., etc., etc.
})
}
}
The compiler knows about this magical type, and does whatever it can to statically verify that the constraints given by the instance aren't violating things that are known to be true (e.g., in the prelude there could be a 'static AddressRangeAttributes{range: 0..1024, attributes: vec![Attribute::NoReadNoWrite]}
to mark those memory ranges as off limits), as well as provide runtime access so that verification can be done for those things that can't be verified at compile time (e.g., there is a global memory allocation that is still live that overlaps the given range, so you can't create a memory mapped region there).
The reason I want to do it via the compiler is because some attributes might affect where the compiler chooses to allocate variables. For example, if I had speed attributes, I could mark that certain address ranges are naturally slower than other ones (like in a NUMA architecture), and with lifetimes I can make statements about how long that slowness lasts, in case I've done some temporary memory mapped I/O tricks.
Anyways, let me know what you guys think.