diff --git a/spec-trait-impl/crates/spec-trait-bin/src/examples/lifetimes2.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/lifetimes2.rs new file mode 100644 index 0000000..c383554 --- /dev/null +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/lifetimes2.rs @@ -0,0 +1,31 @@ +use spec_trait_macro::{spec, when}; + +trait Bad1 { + fn bad1(&self); +} + +impl Bad1 for T { + fn bad1(&self) { + println!("generic"); + } +} + +#[when(T: 'static)] +impl Bad1 for T { + fn bad1(&self) { + println!("'static"); + } +} + +pub fn run() { + println!("\n- Lifetimes 2 Examples:"); + + let static_str: &'static str = &"hello"; + spec! { "hello".bad1(); &'static str } + spec! { "hello".bad1(); &'static str; &'static str: 'static } + spec! { "hello".bad1(); &str; &str: 'static } + spec! { static_str.bad1(); &'static str } + spec! { static_str.bad1(); &'static str; &'static str: 'static } + spec! { static_str.bad1(); &str; &str: 'static } + <&'static str as Bad1>::bad1(&static_str); +} diff --git a/spec-trait-impl/crates/spec-trait-bin/src/examples/mod.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/mod.rs index eced03f..027076b 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/examples/mod.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/mod.rs @@ -2,6 +2,7 @@ mod all; mod base; mod higher; mod lifetimes; +mod lifetimes2; mod order; mod repeating; mod sself; @@ -18,4 +19,5 @@ pub fn run() { higher::run(); order::run(); all::run(); + lifetimes2::run(); } diff --git a/spec-trait-impl/crates/spec-trait-utils/src/specialize.rs b/spec-trait-impl/crates/spec-trait-utils/src/specialize.rs index 0483f2c..5ffea14 100644 --- a/spec-trait-impl/crates/spec-trait-utils/src/specialize.rs +++ b/spec-trait-impl/crates/spec-trait-utils/src/specialize.rs @@ -9,7 +9,7 @@ use proc_macro2::Span; use syn::punctuated::Punctuated; use syn::visit::Visit; use syn::visit_mut::VisitMut; -use syn::{GenericParam, Generics, Ident, LifetimeParam, Type, TypeParam}; +use syn::{GenericParam, Generics, Ident, LifetimeParam, Type, TypeParam, TypeParamBound}; // TODO: infer lifetimes as well @@ -213,7 +213,11 @@ pub fn apply_type_condition( // add new generic types for generic in new_generics { - add_generic_type(generics, &generic); + if is_direct_reference_to_generic(&new_type, &generic) { + add_generic_type_relaxed(generics, &generic); + } else { + add_generic_type(generics, &generic); + } add_generic_type(other_generics, &generic); // TODO: add generic to impl Trait types } @@ -234,6 +238,35 @@ pub fn apply_type_condition( new_type } +fn is_direct_reference_to_generic(ty: &Type, generic: &str) -> bool { + struct Finder<'a> { + generic: &'a str, + found: bool, + } + + impl Visit<'_> for Finder<'_> { + fn visit_type_reference(&mut self, node: &syn::TypeReference) { + if let Type::Path(path) = node.elem.as_ref() + && path.qself.is_none() + && path.path.segments.len() == 1 + && path.path.segments[0].ident == self.generic + { + self.found = true; + return; + } + + syn::visit::visit_type_reference(self, node); + } + } + + let mut finder = Finder { + generic, + found: false, + }; + finder.visit_type(ty); + finder.found +} + pub fn remove_generic(generics: &mut Generics, generic: &str) { generics.params = generics.params .clone() @@ -279,6 +312,20 @@ pub fn add_generic_type(generics: &mut Generics, generic: &str) { })) } +pub fn add_generic_type_relaxed(generics: &mut Generics, generic: &str) { + let mut bounds = Punctuated::new(); + bounds.push(TypeParamBound::Verbatim(quote::quote!(?Sized))); + + generics.params.push(GenericParam::Type(TypeParam { + attrs: vec![], + ident: Ident::new(generic, Span::call_site()), + colon_token: None, + bounds, + eq_token: None, + default: None, + })) +} + pub fn add_generic_lifetime(generics: &mut Generics, generic: &str) { generics.params.push(GenericParam::Lifetime(LifetimeParam { attrs: vec![],