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!(