From d8a313dd422c78fb018cfe4249b526fe3e9dc851 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Fri, 19 Nov 2021 10:35:56 +0100 Subject: allow same-named method generics if they have same bounds --- README.md | 3 +++ src/lib.rs | 25 +++++++++++++++++++++++-- tests/tests.rs | 8 +++++--- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 332de4f..967738e 100644 --- a/README.md +++ b/README.md @@ -117,3 +117,6 @@ trait TraitWithCallback { Note that since in order to be object-safe methods must not have generics, dynamize simply moves the generic from the method to the trait definition. + +If two method type parameters have the same name, dynamize enforces that they +also have the same bounds and only adds the parameter once to the trait. diff --git a/src/lib.rs b/src/lib.rs index 6e88136..5385181 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ #![doc = include_str!("../README.md")] +use std::collections::HashMap; + use proc_macro::TokenStream; use proc_macro2::Group; use quote::format_ident; @@ -8,6 +10,7 @@ use quote::quote_spanned; use quote::ToTokens; use syn::parse_macro_input; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::token::Brace; use syn::token::Gt; use syn::token::Lt; @@ -171,6 +174,14 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream { items: Vec::new(), }; + let mut generic_map = HashMap::new(); + + for generic in &dyn_trait.generics.params { + if let GenericParam::Type(type_param) = generic { + generic_map.insert(type_param.ident.clone(), type_param.bounds.clone()); + } + } + for (signature, parsed_method) in objectifiable_methods { let mut new_method = TraitItemMethod { attrs: Vec::new(), @@ -235,8 +246,18 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream { // FUTURE: use Vec::drain_filter once it's stable let mut i = 0; while i < params.len() { - if matches!(params[i], GenericParam::Type(_)) { - dyn_trait.generics.params.push(params.remove(i)); + if let GenericParam::Type(type_param) = ¶ms[i] { + if let Some(bounds) = generic_map.get(&type_param.ident) { + if *bounds == type_param.bounds { + params.remove(i); + continue; + } else { + return abort!(type_param.span(), "dynamize failure: there exists a same-named method generic with different bounds"); + } + } else { + generic_map.insert(type_param.ident.clone(), type_param.bounds.clone()); + dyn_trait.generics.params.push(params.remove(i)); + } } else { i += 1; } diff --git a/tests/tests.rs b/tests/tests.rs index 6bccc04..4d09847 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -100,12 +100,14 @@ fn test1, X, A, B, C>(some: T) { trait Buz { type C: Into; - fn foobar(&self, x: X) -> Result; + fn foobar1(&self, x: X) -> Result; + fn foobar2(&self) -> Result; } -fn test2, X, A>(some: T, x: X) -> Result { +fn test2, X, A>(some: T, x: X) { let dyn_trait: &dyn DynBuz = &some; - dyn_trait.foobar(x) + let _: Result = dyn_trait.foobar1(x); + let _: Result = dyn_trait.foobar2(); } #[dynamize::dynamize] -- cgit v1.2.3