From 634dbd2d7861225181c58416ecda11d04498377e Mon Sep 17 00:00:00 2001 From: Samson <16504129+sagudev@users.noreply.github.com> Date: Thu, 3 Oct 2024 14:58:34 +0200 Subject: [PATCH] Replace `ShouldNotImplDomObject` with `NoDomObjectInDomObject` (#33627) This is https://github.com/jdm/servo/pull/1 rebased on servo/servo Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- components/domobject_derive/lib.rs | 53 +++++++++++++++++++----------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/components/domobject_derive/lib.rs b/components/domobject_derive/lib.rs index 1c762a80967..ac99c4fa16a 100644 --- a/components/domobject_derive/lib.rs +++ b/components/domobject_derive/lib.rs @@ -5,8 +5,9 @@ #![recursion_limit = "128"] use quote::{quote, TokenStreamExt}; -use syn::parse_quote; +/// First field of DomObject must be either reflector or another dom_struct, +/// all other fields must not implement DomObject #[proc_macro_derive(DomObject)] pub fn expand_token_stream(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = syn::parse(input).unwrap(); @@ -65,33 +66,45 @@ fn expand_dom_object(input: syn::DeriveInput) -> proc_macro2::TokenStream { crate::DomObject::reflector(self) == crate::DomObject::reflector(other) } } + + // Generic trait with a blanket impl over `()` for all types. + // becomes ambiguous if impl + trait NoDomObjectInDomObject { + // Required for actually being able to reference the trait. + fn some_item() {} + } + + impl NoDomObjectInDomObject<()> for T {} + + // Used for the specialized impl when DomObject is implemented. + #[allow(dead_code)] + struct Invalid; + // forbids DomObject + impl NoDomObjectInDomObject for T where T: ?Sized + crate::DomObject {} }; let mut params = proc_macro2::TokenStream::new(); - params.append_separated(input.generics.type_params().map(|param| ¶m.ident), ", "); + params.append_separated( + input.generics.type_params().map(|param| ¶m.ident), + quote! {,}, + ); - // For each field in the struct, we implement ShouldNotImplDomObject for a - // pair of all the type parameters of the DomObject and and the field type. - // This allows us to support parameterized DOM objects - // such as IteratorIterable. - items.append_all(field_types.iter().map(|ty| { + items.append_all(field_types.iter().enumerate().map(|(i, ty)| { + let s = syn::Ident::new(&format!("S{i}"), proc_macro2::Span::call_site()); quote! { - impl #impl_generics ShouldNotImplDomObject for ((#params), #ty) #where_clause {} + struct #s<#params>(#params); + + impl #impl_generics #s<#params> #where_clause { + fn f() { + // If there is only one specialized trait impl, type inference with + // `_` can be resolved and this can compile. Fails to compile if + // ty implements `NoDomObjectInDomObject`. + let _ = <#ty as NoDomObjectInDomObject<_>>::some_item; + } + } } })); - let mut generics = input.generics.clone(); - generics.params.push(parse_quote!( - __T: crate::DomObject - )); - - let (impl_generics, _, where_clause) = generics.split_for_impl(); - - items.append_all(quote! { - trait ShouldNotImplDomObject {} - impl #impl_generics ShouldNotImplDomObject for ((#params), __T) #where_clause {} - }); - let dummy_const = syn::Ident::new( &format!("_IMPL_DOMOBJECT_FOR_{}", name), proc_macro2::Span::call_site(),