From 86e1fdae8ecd8fac154696db1f811cb59aee6ef7 Mon Sep 17 00:00:00 2001 From: Andrey Kutejko Date: Sun, 21 Jan 2024 14:40:27 +0100 Subject: [PATCH] Support generic types in `glib::object_subclass` and `glib::object_interface` macros --- glib-macros/src/object_interface_attribute.rs | 43 +++++++----- glib-macros/src/object_subclass_attribute.rs | 55 ++++++++------- glib-macros/tests/object_interface_generic.rs | 68 +++++++++++++++++++ glib-macros/tests/object_subclass_generic.rs | 26 +++++++ 4 files changed, 151 insertions(+), 41 deletions(-) create mode 100644 glib-macros/tests/object_interface_generic.rs create mode 100644 glib-macros/tests/object_subclass_generic.rs diff --git a/glib-macros/src/object_interface_attribute.rs b/glib-macros/src/object_interface_attribute.rs index 8222e1d58491..807a10d6ae8e 100644 --- a/glib-macros/src/object_interface_attribute.rs +++ b/glib-macros/src/object_interface_attribute.rs @@ -22,16 +22,15 @@ pub fn impl_object_interface(input: &mut syn::ItemImpl) -> TokenStream { .. } = input; - let self_ty_as_ident = match &**self_ty { - syn::Type::Path(syn::TypePath { path, .. }) => path.require_ident(), - _ => Err(syn::Error::new( + let Some(self_ty_ident) = (match &**self_ty { + syn::Type::Path(syn::TypePath { path, .. }) => path.segments.last().map(|s| &s.ident), + _ => None, + }) else { + return syn::Error::new( syn::spanned::Spanned::span(self_ty), - "expected this path to be an identifier", - )), - }; - let self_ty = match self_ty_as_ident { - Ok(ident) => ident, - Err(e) => return e.to_compile_error(), + "expected this path to have an identifier", + ) + .to_compile_error(); }; let mut plugin_type = NestedMetaItem::::new("plugin_type").value_required(); @@ -46,7 +45,7 @@ pub fn impl_object_interface(input: &mut syn::ItemImpl) -> TokenStream { let register_object_interface = match found { Err(e) => return e.to_compile_error(), - Ok(None) => register_object_interface_as_static(&crate_ident, self_ty), + Ok(None) => register_object_interface_as_static(&crate_ident, generics, self_ty), Ok(Some(_)) => { // remove attribute 'object_interface_dynamic' from the attribute list because it is not a real proc_macro_attribute attrs.retain(|attr| !attr.path().is_ident("object_interface_dynamic")); @@ -57,7 +56,9 @@ pub fn impl_object_interface(input: &mut syn::ItemImpl) -> TokenStream { let lazy_registration = lazy_registration.value.map(|b| b.value).unwrap_or_default(); register_object_interface_as_dynamic( &crate_ident, + generics, self_ty, + self_ty_ident, plugin_ty, lazy_registration, ) @@ -94,7 +95,7 @@ pub fn impl_object_interface(input: &mut syn::ItemImpl) -> TokenStream { #(#items)* } - unsafe impl #crate_ident::subclass::interface::ObjectInterfaceType for #self_ty { + unsafe impl #generics #crate_ident::subclass::interface::ObjectInterfaceType for #self_ty { #[inline] fn type_() -> #crate_ident::Type { Self::register_interface() @@ -108,11 +109,12 @@ pub fn impl_object_interface(input: &mut syn::ItemImpl) -> TokenStream { // Registers the object interface as a static type. fn register_object_interface_as_static( crate_ident: &TokenStream, - self_ty: &syn::Ident, + generics: &syn::Generics, + self_ty: &syn::Type, ) -> TokenStream { // registers the interface on first use (lazy registration). quote! { - impl #self_ty { + impl #generics #self_ty { /// Registers the interface only once. #[inline] fn register_interface() -> #crate_ident::Type { @@ -135,7 +137,9 @@ fn register_object_interface_as_static( // An object interface can be reregistered as a dynamic type. fn register_object_interface_as_dynamic( crate_ident: &TokenStream, - self_ty: &syn::Ident, + generics: &syn::Generics, + self_ty: &syn::Type, + self_ty_ident: &syn::Ident, plugin_ty: TokenStream, lazy_registration: bool, ) -> TokenStream { @@ -147,7 +151,7 @@ fn register_object_interface_as_dynamic( // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the object interface has been registered. // the registration status type. - let registration_status_type = format_ident!("{}RegistrationStatus", self_ty); + let registration_status_type = format_ident!("{}RegistrationStatus", self_ty_ident); // name of the static variable to store the registration status. let registration_status = format_ident!( "{}", @@ -162,7 +166,7 @@ fn register_object_interface_as_dynamic( /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data. static #registration_status: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); - impl #self_ty { + impl #generics #self_ty { /// Registers the object interface as a dynamic type within the plugin only once. /// Plugin must have been used at least once. /// Do nothing if plugin has never been used or if the object interface is already registered as a dynamic type. @@ -232,13 +236,16 @@ fn register_object_interface_as_dynamic( // registers immediately the object interface as a dynamic type. // name of the static variable to store the GLib type. - let gtype_status = format_ident!("{}_G_TYPE", self_ty.to_string().to_shouty_snake_case()); + let gtype_status = format_ident!( + "{}_G_TYPE", + self_ty_ident.to_string().to_shouty_snake_case() + ); quote! { /// The GLib type which can be safely shared between threads. static #gtype_status: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); - impl #self_ty { + impl #generics #self_ty { /// Do nothing as the object interface has been registered on implementation load. #[inline] fn register_interface() -> #crate_ident::Type { diff --git a/glib-macros/src/object_subclass_attribute.rs b/glib-macros/src/object_subclass_attribute.rs index 197ff72dff8a..4875b8978171 100644 --- a/glib-macros/src/object_subclass_attribute.rs +++ b/glib-macros/src/object_subclass_attribute.rs @@ -21,16 +21,15 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { .. } = input; - let self_ty_as_ident = match &**self_ty { - syn::Type::Path(syn::TypePath { path, .. }) => path.require_ident(), - _ => Err(syn::Error::new( + let Some(self_ty_ident) = (match &**self_ty { + syn::Type::Path(syn::TypePath { path, .. }) => path.segments.last().map(|s| &s.ident), + _ => None, + }) else { + return syn::Error::new( syn::spanned::Spanned::span(self_ty), - "expected this path to be an identifier", - )), - }; - let self_ty = match self_ty_as_ident { - Ok(ident) => ident, - Err(e) => return e.to_compile_error(), + "expected this path to have an identifier", + ) + .to_compile_error(); }; let mut plugin_type = NestedMetaItem::::new("plugin_type").value_required(); @@ -45,7 +44,7 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { let register_object_subclass = match found { Err(e) => return e.to_compile_error(), - Ok(None) => register_object_subclass_as_static(&crate_ident, self_ty), + Ok(None) => register_object_subclass_as_static(&crate_ident, generics, self_ty), Ok(Some(_)) => { // remove attribute 'object_subclass_dynamic' from the attribute list because it is not a real proc_macro_attribute attrs.retain(|attr| !attr.path().is_ident("object_subclass_dynamic")); @@ -54,7 +53,14 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { .map(|p| p.into_token_stream()) .unwrap_or(quote!(#crate_ident::TypeModule)); let lazy_registration = lazy_registration.value.map(|b| b.value).unwrap_or_default(); - register_object_subclass_as_dynamic(&crate_ident, self_ty, plugin_ty, lazy_registration) + register_object_subclass_as_dynamic( + &crate_ident, + generics, + self_ty, + self_ty_ident, + plugin_ty, + lazy_registration, + ) } }; @@ -130,7 +136,7 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { #(#items)* } - unsafe impl #crate_ident::subclass::types::ObjectSubclassType for #self_ty { + unsafe impl #generics #crate_ident::subclass::types::ObjectSubclassType for #self_ty { #[inline] fn type_data() -> ::std::ptr::NonNull<#crate_ident::subclass::TypeData> { static mut DATA: #crate_ident::subclass::TypeData = @@ -154,7 +160,7 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { #register_object_subclass #[doc(hidden)] - impl #crate_ident::subclass::types::FromObject for #self_ty { + impl #generics #crate_ident::subclass::types::FromObject for #self_ty { type FromObjectType = ::Type; #[inline] fn from_object(obj: &Self::FromObjectType) -> &Self { @@ -163,7 +169,7 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { } #[doc(hidden)] - impl #crate_ident::clone::Downgrade for #self_ty { + impl #generics #crate_ident::clone::Downgrade for #self_ty { type Weak = #crate_ident::subclass::ObjectImplWeakRef<#self_ty>; #[inline] @@ -173,7 +179,7 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { } } - impl #self_ty { + impl #generics #self_ty { #[inline] pub fn downgrade(&self) -> ::Weak { #crate_ident::clone::Downgrade::downgrade(self) @@ -181,7 +187,7 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { } #[doc(hidden)] - impl ::std::borrow::ToOwned for #self_ty { + impl #generics ::std::borrow::ToOwned for #self_ty { type Owned = #crate_ident::subclass::ObjectImplRef<#self_ty>; #[inline] @@ -191,7 +197,7 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { } #[doc(hidden)] - impl ::std::borrow::Borrow<#self_ty> for #crate_ident::subclass::ObjectImplRef<#self_ty> { + impl #generics ::std::borrow::Borrow<#self_ty> for #crate_ident::subclass::ObjectImplRef<#self_ty> { #[inline] fn borrow(&self) -> &#self_ty { self @@ -203,11 +209,12 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> TokenStream { // Registers the object subclass as a static type. fn register_object_subclass_as_static( crate_ident: &TokenStream, - self_ty: &syn::Ident, + generics: &syn::Generics, + self_ty: &syn::Type, ) -> TokenStream { // registers the object subclass on first use (lazy registration). quote! { - impl #self_ty { + impl #generics #self_ty { /// Registers the type only once. #[inline] fn register_type() { @@ -225,7 +232,9 @@ fn register_object_subclass_as_static( // An object subclass can be reregistered as a dynamic type. fn register_object_subclass_as_dynamic( crate_ident: &TokenStream, - self_ty: &syn::Ident, + generics: &syn::Generics, + self_ty: &syn::Type, + self_ty_ident: &syn::Ident, plugin_ty: TokenStream, lazy_registration: bool, ) -> TokenStream { @@ -237,7 +246,7 @@ fn register_object_subclass_as_dynamic( // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the object subclass has been registered. // the registration status type. - let registration_status_type = format_ident!("{}RegistrationStatus", self_ty); + let registration_status_type = format_ident!("{}RegistrationStatus", self_ty_ident); // name of the static variable to store the registration status. let registration_status = format_ident!( "{}", @@ -252,7 +261,7 @@ fn register_object_subclass_as_dynamic( /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data. static #registration_status: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); - impl #self_ty { + impl #generics #self_ty { /// Registers the object subclass as a dynamic type within the plugin only once. /// Plugin must have been used at least once. /// Do nothing if plugin has never been used or if the object subclass is already registered as a dynamic type. @@ -321,7 +330,7 @@ fn register_object_subclass_as_dynamic( // registers immediately the object subclass as a dynamic type. quote! { - impl #self_ty { + impl #generics #self_ty { /// Do nothing as the object subclass has been registered on implementation load. #[inline] fn register_type() { } diff --git a/glib-macros/tests/object_interface_generic.rs b/glib-macros/tests/object_interface_generic.rs new file mode 100644 index 000000000000..3c6abead21e7 --- /dev/null +++ b/glib-macros/tests/object_interface_generic.rs @@ -0,0 +1,68 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::subclass::prelude::*; + +pub mod imp { + use std::marker::PhantomData; + + use super::*; + + #[repr(C)] + pub struct MyGenericInterface { + parent: glib::gobject_ffi::GTypeInterface, + marker: PhantomData, + } + + impl Clone for MyGenericInterface { + fn clone(&self) -> Self { + Self { + parent: self.parent, + marker: self.marker, + } + } + } + + impl Copy for MyGenericInterface {} + + #[glib::object_interface] + unsafe impl ObjectInterface for MyGenericInterface { + const NAME: &'static str = "MyGenericInterface"; + } + + pub trait MyGenericInterfaceImpl: ObjectImpl + ObjectSubclass {} + + pub struct MyGenericType { + marker: PhantomData, + } + + #[glib::object_subclass] + impl ObjectSubclass for MyGenericType { + const NAME: &'static str = "MyGenericType"; + type Type = super::MyGenericType; + type Interfaces = (super::MyGenericInterface,); + + fn new() -> Self { + Self { + marker: PhantomData, + } + } + } + + impl ObjectImpl for MyGenericType {} + + impl MyGenericInterfaceImpl for MyGenericType {} + + pub trait MyGenericTypeImpl: ObjectImpl + ObjectSubclass {} +} + +glib::wrapper! { + pub struct MyGenericInterface(ObjectInterface>); +} + +unsafe impl, T> IsImplementable for MyGenericInterface {} + +glib::wrapper! { + pub struct MyGenericType(ObjectSubclass>) @implements MyGenericInterface; +} + +unsafe impl, T> IsSubclassable for MyGenericType {} diff --git a/glib-macros/tests/object_subclass_generic.rs b/glib-macros/tests/object_subclass_generic.rs new file mode 100644 index 000000000000..122085cc987c --- /dev/null +++ b/glib-macros/tests/object_subclass_generic.rs @@ -0,0 +1,26 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::subclass::prelude::*; + +pub mod imp { + use super::*; + use std::marker::PhantomData; + + pub struct MyGenericType(PhantomData); + + #[glib::object_subclass] + impl ObjectSubclass for MyGenericType { + const NAME: &'static str = "MyGenericType"; + type Type = super::MyGenericType; + + fn new() -> Self { + MyGenericType(PhantomData::) + } + } + + impl ObjectImpl for MyGenericType {} +} + +glib::wrapper! { + pub struct MyGenericType(ObjectSubclass>); +}