diff --git a/src/lib.rs b/src/lib.rs
index 4cc9d61..c419c8b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -47,76 +47,6 @@
#![doc(test(attr(allow(dead_code))))]
#![doc(test(attr(allow(unused_variables))))]
-/// A derive macro for method-based accesses to volatile structures.
-///
-/// This macro allows you to access the fields of a volatile structure via methods that enforce access limitations.
-/// It is also more easily chainable than [`map_field`].
-///
-///
-///
-/// This macro generates and implements a new `{T}VolatileFieldAccess` trait, that you have to import if used from other modules.
-/// Currently, the trait is only implemented for `VolatilePtr<'_, _, ReadWrite>`.
-///
-///
-///
-/// # Examples
-///
-/// ```
-/// use volatile::access::ReadOnly;
-/// use volatile::{VolatileFieldAccess, VolatileRef};
-///
-/// #[repr(C)]
-/// #[derive(VolatileFieldAccess, Default)]
-/// pub struct DeviceConfig {
-/// feature_select: u32,
-/// #[access(ReadOnly)]
-/// feature: u32,
-/// }
-///
-/// let mut device_config = DeviceConfig::default();
-/// let mut volatile_ref = VolatileRef::from_mut_ref(&mut device_config);
-/// let volatile_ptr = volatile_ref.as_mut_ptr();
-///
-/// volatile_ptr.feature_select().write(42);
-/// assert_eq!(volatile_ptr.feature_select().read(), 42);
-///
-/// // This does not compile, because we specified `#[access(ReadOnly)]` for this field.
-/// // volatile_ptr.feature().write(42);
-///
-/// // A real device might have changed the value, though.
-/// assert_eq!(volatile_ptr.feature().read(), 0);
-/// ```
-///
-/// # Details
-///
-/// This macro generates a new trait (`{T}VolatileFieldAccess`) and implements it for `VolatilePtr<'a, T, ReadWrite>`.
-/// The example above results in (roughly) the following code:
-///
-/// ```
-/// # #[repr(C)]
-/// # pub struct DeviceConfig {
-/// # feature_select: u32,
-/// # feature: u32,
-/// # }
-/// use volatile::access::{ReadOnly, ReadWrite};
-/// use volatile::{map_field, VolatilePtr};
-///
-/// pub trait DeviceConfigVolatileFieldAccess<'a> {
-/// fn feature_select(self) -> VolatilePtr<'a, u32, ReadWrite>;
-///
-/// fn feature(self) -> VolatilePtr<'a, u32, ReadOnly>;
-/// }
-///
-/// impl<'a> DeviceConfigVolatileFieldAccess<'a> for VolatilePtr<'a, DeviceConfig, ReadWrite> {
-/// fn feature_select(self) -> VolatilePtr<'a, u32, ReadWrite> {
-/// map_field!(self.feature_select).restrict()
-/// }
-///
-/// fn feature(self) -> VolatilePtr<'a, u32, ReadOnly> {
-/// map_field!(self.feature).restrict()
-/// }
-/// }
-/// ```
#[cfg(feature = "derive")]
pub use volatile_macro::VolatileFieldAccess;
diff --git a/volatile-macro/Cargo.toml b/volatile-macro/Cargo.toml
index a592811..8f17c47 100644
--- a/volatile-macro/Cargo.toml
+++ b/volatile-macro/Cargo.toml
@@ -16,3 +16,6 @@ proc-macro = true
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["full"] }
+
+[dev-dependencies]
+volatile = { version = "=0.5.4", path = "..", features = ["derive"] }
diff --git a/volatile-macro/src/lib.rs b/volatile-macro/src/lib.rs
index 8e2e175..2fe9cad 100644
--- a/volatile-macro/src/lib.rs
+++ b/volatile-macro/src/lib.rs
@@ -1,3 +1,6 @@
+#![doc(test(attr(deny(warnings))))]
+#![doc(test(attr(allow(dead_code))))]
+
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
@@ -11,6 +14,92 @@ macro_rules! bail {
mod volatile;
+/// A derive macro for method-based accesses to volatile structures.
+///
+/// This macro allows you to access the fields of a volatile structure via methods that enforce access limitations.
+/// It is also more easily chainable than `map_field`.
+///
+///
+///
+/// This macro generates and implements a new `{T}VolatileFieldAccess` trait, that you have to import if used from other modules.
+/// Currently, the trait is only implemented for `VolatilePtr<'_, _, ReadWrite>`.
+///
+///
+///
+/// # Examples
+///
+/// ```
+/// use volatile::access::ReadOnly;
+/// use volatile::{VolatileFieldAccess, VolatileRef};
+///
+/// #[repr(C)]
+/// #[derive(VolatileFieldAccess, Default)]
+/// pub struct DeviceConfig {
+/// feature_select: u32,
+/// #[access(ReadOnly)]
+/// feature: u32,
+/// }
+///
+/// let mut device_config = DeviceConfig::default();
+/// let mut volatile_ref = VolatileRef::from_mut_ref(&mut device_config);
+/// let volatile_ptr = volatile_ref.as_mut_ptr();
+///
+/// volatile_ptr.feature_select().write(42);
+/// assert_eq!(volatile_ptr.feature_select().read(), 42);
+///
+/// // This does not compile, because we specified `#[access(ReadOnly)]` for this field.
+/// // volatile_ptr.feature().write(42);
+///
+/// // A real device might have changed the value, though.
+/// assert_eq!(volatile_ptr.feature().read(), 0);
+///
+/// // You can also use shared references.
+/// let volatile_ptr = volatile_ref.as_ptr();
+/// assert_eq!(volatile_ptr.feature_select().read(), 42);
+/// // This does not compile, because `volatile_ptr` is `ReadOnly`.
+/// // volatile_ptr.feature_select().write(42);
+/// ```
+///
+/// # Details
+///
+/// This macro generates a new trait (`{T}VolatileFieldAccess`) and implements it for `VolatilePtr<'a, T, ReadWrite>`.
+/// The example above results in (roughly) the following code:
+///
+/// ```
+/// # #[repr(C)]
+/// # pub struct DeviceConfig {
+/// # feature_select: u32,
+/// # feature: u32,
+/// # }
+/// use volatile::access::{ReadOnly, ReadWrite, RestrictAccess};
+/// use volatile::{map_field, VolatilePtr};
+///
+/// pub trait DeviceConfigVolatileFieldAccess<'a, A> {
+/// fn feature_select(self) -> VolatilePtr<'a, u32, A::Restricted>
+/// where
+/// A: RestrictAccess;
+///
+/// fn feature(self) -> VolatilePtr<'a, u32, A::Restricted>
+/// where
+/// A: RestrictAccess;
+/// }
+///
+/// impl<'a, A> DeviceConfigVolatileFieldAccess<'a, A> for VolatilePtr<'a, DeviceConfig, A> {
+/// fn feature_select(self) -> VolatilePtr<'a, u32, A::Restricted>
+/// where
+/// A: RestrictAccess
+/// {
+/// map_field!(self.feature_select).restrict()
+/// }
+///
+/// fn feature(self) -> VolatilePtr<'a, u32, A::Restricted>
+/// where
+/// A: RestrictAccess
+/// {
+/// map_field!(self.feature).restrict()
+/// }
+/// }
+/// ```
#[proc_macro_derive(VolatileFieldAccess, attributes(access))]
pub fn derive_volatile(item: TokenStream) -> TokenStream {
match volatile::derive_volatile(parse_macro_input!(item)) {
diff --git a/volatile-macro/src/volatile.rs b/volatile-macro/src/volatile.rs
index ecff9f6..a80f635 100644
--- a/volatile-macro/src/volatile.rs
+++ b/volatile-macro/src/volatile.rs
@@ -84,7 +84,9 @@ fn parse_input(input: &ItemStruct) -> Result {
}
let sig = parse_quote! {
- fn #ident(self) -> ::volatile::VolatilePtr<'a, #ty, #access>
+ fn #ident(self) -> ::volatile::VolatilePtr<'a, #ty, A::Restricted>
+ where
+ A: ::volatile::access::RestrictAccess<#access>
};
sigs.push(sig);
}
@@ -112,7 +114,7 @@ fn emit_trait(
parse_quote! {
#(#attrs)*
#[allow(non_camel_case_types)]
- #vis trait #trait_ident <'a> {
+ #vis trait #trait_ident <'a, A> {
#(
#(#method_attrs)*
#sigs;
@@ -133,9 +135,10 @@ fn emit_impl(
parse_quote! {
#[automatically_derived]
- impl<'a> #trait_ident<'a> for ::volatile::VolatilePtr<'a, #struct_ident, ::volatile::access::ReadWrite> {
+ impl<'a, A> #trait_ident<'a, A> for ::volatile::VolatilePtr<'a, #struct_ident, A> {
#(
- #sigs {
+ #sigs,
+ {
::volatile::map_field!(self.#fields).restrict()
}
)*
@@ -183,24 +186,34 @@ mod tests {
///
/// This is a wonderful struct.
#[allow(non_camel_case_types)]
- pub trait DeviceConfigVolatileFieldAccess<'a> {
- fn feature_select(self) -> ::volatile::VolatilePtr<'a, u32, ::volatile::access::ReadWrite>;
+ pub trait DeviceConfigVolatileFieldAccess<'a, A> {
+ fn feature_select(self) -> ::volatile::VolatilePtr<'a, u32, A::Restricted>
+ where
+ A: ::volatile::access::RestrictAccess<::volatile::access::ReadWrite>;
/// Feature.
///
/// This is a good field.
- fn feature(self) -> ::volatile::VolatilePtr<'a, u32, ReadOnly>;
+ fn feature(self) -> ::volatile::VolatilePtr<'a, u32, A::Restricted>
+ where
+ A: ::volatile::access::RestrictAccess;
}
};
let expected_impl = quote! {
#[automatically_derived]
- impl<'a> DeviceConfigVolatileFieldAccess<'a> for ::volatile::VolatilePtr<'a, DeviceConfig, ::volatile::access::ReadWrite> {
- fn feature_select(self) -> ::volatile::VolatilePtr<'a, u32, ::volatile::access::ReadWrite> {
+ impl<'a, A> DeviceConfigVolatileFieldAccess<'a, A> for ::volatile::VolatilePtr<'a, DeviceConfig, A> {
+ fn feature_select(self) -> ::volatile::VolatilePtr<'a, u32, A::Restricted>
+ where
+ A: ::volatile::access::RestrictAccess<::volatile::access::ReadWrite>,
+ {
::volatile::map_field!(self.feature_select).restrict()
}
- fn feature(self) -> ::volatile::VolatilePtr<'a, u32, ReadOnly> {
+ fn feature(self) -> ::volatile::VolatilePtr<'a, u32, A::Restricted>
+ where
+ A: ::volatile::access::RestrictAccess,
+ {
::volatile::map_field!(self.feature).restrict()
}
}
@@ -230,12 +243,12 @@ mod tests {
let expected_trait = quote! {
#[allow(non_camel_case_types)]
- pub trait DeviceConfigVolatileFieldAccess<'a> {}
+ pub trait DeviceConfigVolatileFieldAccess<'a, A> {}
};
let expected_impl = quote! {
#[automatically_derived]
- impl<'a> DeviceConfigVolatileFieldAccess<'a> for ::volatile::VolatilePtr<'a, DeviceConfig, ::volatile::access::ReadWrite> {}
+ impl<'a, A> DeviceConfigVolatileFieldAccess<'a, A> for ::volatile::VolatilePtr<'a, DeviceConfig, A> {}
};
assert_eq!(