diff --git a/.travis.yml b/.travis.yml index f2c919b..6e91a9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: script: # `--lib` added to prevent doctests from being compiled. # This is due to `unstable_const` requiring extra `feature(...)` directives # which the doctests do not have. - - cargo test --verbose --features unstable_const --lib + - cargo test --verbose --all-features --lib - env: RUSTFMT rust: 1.36.0 diff --git a/Cargo.toml b/Cargo.toml index cabe0ce..646111d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ doc-comment = "0.3" [features] default = [] unstable_const = [] +unstable_raw = [] diff --git a/README.md b/README.md index 647302f..e5a5fdb 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,9 @@ fn main() { } ``` -## Usage in constants ## +## Feature flags ## + +### Usage in constants ### `memoffset` has **experimental** support for compile-time `offset_of!` on a nightly compiler. In order to use it, you must enable the `unstable_const` crate feature and several compiler features. @@ -78,4 +80,9 @@ struct Foo { } let foo = [0; offset_of!(Foo, b)] -``` \ No newline at end of file +``` + +### Raw references ### +Recent nightlies support [a way to create raw pointers](https://github.com/rust-lang/rust/issues/73394) that avoids creating intermediate safe references. +`memoffset` can make use of that feature to avoid what is technically Undefined Behavior. +Use the `unstable_raw` feature to enable this. diff --git a/ci/miri.sh b/ci/miri.sh index 8873a2c..03ebd73 100644 --- a/ci/miri.sh +++ b/ci/miri.sh @@ -8,3 +8,4 @@ rustup component add miri cargo miri setup cargo miri test +cargo miri test --all-features diff --git a/src/lib.rs b/src/lib.rs index 63960ae..430b597 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,10 @@ const_raw_ptr_deref ) )] +#![cfg_attr( + feature = "unstable_raw", + feature(raw_ref_macros) +)] #[macro_use] #[cfg(doctests)] @@ -79,7 +83,6 @@ doctest!("../README.md"); // Doing this enables this crate to function under both std and no-std crates. #[doc(hidden)] pub use core::mem; - #[doc(hidden)] pub use core::ptr; diff --git a/src/offset_of.rs b/src/offset_of.rs index 1607211..2309a13 100644 --- a/src/offset_of.rs +++ b/src/offset_of.rs @@ -63,6 +63,31 @@ macro_rules! _memoffset__field_check { /// /// The `base` pointer *must not* be dangling, but it *may* point to /// uninitialized memory. +#[cfg(feature = "unstable_raw")] // Correct variant that uses `raw_const!`. +#[macro_export(local_inner_macros)] +macro_rules! raw_field { + ($base:expr, $parent:path, $field:tt) => {{ + _memoffset__field_check!($parent, $field); + let base_ptr: *const $parent = $base; + + // Get the field address. This is UB because we are creating a reference to + // the uninitialized field. Will be updated to use `&raw` before rustc + // starts exploiting such UB. + // Crucially, we know that this will not trigger a deref coercion because + // of the `field_check!` we did above. + #[allow(unused_unsafe)] // for when the macro is used in an unsafe block + unsafe { + $crate::ptr::raw_const!((*base_ptr).$field) + } + }}; +} + +/// Computes a const raw pointer to the given field of the given base pointer +/// to the given parent type. +/// +/// The `base` pointer *must not* be dangling, but it *may* point to +/// uninitialized memory. +#[cfg(not(feature = "unstable_raw"))] // Incorrect (UB) variant that creates an intermediate reference. #[macro_export(local_inner_macros)] macro_rules! raw_field { ($base:expr, $parent:path, $field:tt) => {{