diff --git a/spec-trait-impl/crates/spec-trait-bin/src/all.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/all.rs similarity index 99% rename from spec-trait-impl/crates/spec-trait-bin/src/all.rs rename to spec-trait-impl/crates/spec-trait-bin/src/examples/all.rs index 932cc00..7be4d13 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/all.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/all.rs @@ -294,7 +294,7 @@ impl Foo4 for Vec { } pub fn run() { - println!("\n- All Cases:"); + println!("\n- All Examples:"); let zst = ZST; let zst2 = ZST2; diff --git a/spec-trait-impl/crates/spec-trait-bin/src/base.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/base.rs similarity index 82% rename from spec-trait-impl/crates/spec-trait-bin/src/base.rs rename to spec-trait-impl/crates/spec-trait-bin/src/examples/base.rs index 613ebff..3d681fd 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/base.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/base.rs @@ -40,8 +40,15 @@ impl BaseTrait for BaseType { } } +#[when(not(T = &BaseType))] +impl BaseTrait for BaseType { + fn base_method(&self, _x: T) { + println!("T is not &BaseType"); + } +} + pub fn run() { - println!("\n- Base Cases:"); + println!("\n- Base Examples:"); let x = BaseType; @@ -49,5 +56,6 @@ pub fn run() { spec! { x.base_method(1.2f32); BaseType; [f32]; f32: Copy } // T implements Copy spec! { x.base_method(1i32); BaseType; [i32] } // T is String or i32 spec! { x.base_method(vec![1, 2, 3]); BaseType; [Vec]; i32: Copy } // T is a Vec of Copy + spec! { x.base_method(1i8); BaseType; [i8] } // T is not &BaseType spec! { x.base_method(&x); BaseType; [&BaseType] } // Default } diff --git a/spec-trait-impl/crates/spec-trait-bin/src/examples/higher.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/higher.rs new file mode 100644 index 0000000..deb2ed3 --- /dev/null +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/higher.rs @@ -0,0 +1,33 @@ +use spec_trait_macro::{spec, when}; + +struct BaseType; + +#[allow(dead_code)] +trait HigherOrderSpecTrait { + fn higher_order_spec_method(&self, f: F, arg: T) -> T; +} + +#[when(F = for<'a> fn(&'a T) -> T)] +impl HigherOrderSpecTrait for BaseType { + fn higher_order_spec_method(&self, f: F, arg: T) -> T { + println!("T is generic"); + f(&arg) + } +} + +#[when(all(T = i32, F = for<'a> fn(&'a T) -> T))] +impl HigherOrderSpecTrait for BaseType { + fn higher_order_spec_method(&self, f: F, arg: T) -> T { + println!("T is i32"); + f(&arg) + } +} + +pub fn run() { + println!("\n- Higher Order Specialization Examples:"); + + let x = BaseType; + + let _: i32 = spec! { x.higher_order_spec_method(|v: &i32| *v, 1i32); BaseType; [for<'a> fn(&'a i32) -> i32, i32] }; // -> "T is i32" + let _: u32 = spec! { x.higher_order_spec_method(|v: &u32| *v, 1u32); BaseType; [for<'a> fn(&'a u32) -> u32, u32] }; // -> "T is generic" +} diff --git a/spec-trait-impl/crates/spec-trait-bin/src/lifetimes.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/lifetimes.rs similarity index 96% rename from spec-trait-impl/crates/spec-trait-bin/src/lifetimes.rs rename to spec-trait-impl/crates/spec-trait-bin/src/examples/lifetimes.rs index 30143da..a6f429e 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/lifetimes.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/lifetimes.rs @@ -27,7 +27,7 @@ impl LifetimesTrait for BaseType { } pub fn run() { - println!("\n- Lifetimes Cases:"); + println!("\n- Lifetimes Examples:"); let x = BaseType; 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 new file mode 100644 index 0000000..bda4c07 --- /dev/null +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/mod.rs @@ -0,0 +1,21 @@ +mod all; +mod base; +mod lifetimes; +mod nested; +mod order; +mod sself; +mod traits; +mod types; +mod higher; + +pub fn run() { + base::run(); + types::run(); + traits::run(); + lifetimes::run(); + nested::run(); + sself::run(); + higher::run(); + order::run(); + all::run(); +} diff --git a/spec-trait-impl/crates/spec-trait-bin/src/nested.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/nested.rs similarity index 95% rename from spec-trait-impl/crates/spec-trait-bin/src/nested.rs rename to spec-trait-impl/crates/spec-trait-bin/src/examples/nested.rs index f71b641..e6521bd 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/nested.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/nested.rs @@ -27,7 +27,7 @@ impl NestedTrait for BaseType { } pub fn run() { - println!("\n- Nested Cases:"); + println!("\n- Nested Examples:"); let x = BaseType; diff --git a/spec-trait-impl/crates/spec-trait-bin/src/examples/order.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/order.rs new file mode 100644 index 0000000..fad7c1c --- /dev/null +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/order.rs @@ -0,0 +1,29 @@ +use spec_trait_macro::{spec, when}; + +pub fn run() { + println!("\n- Order Examples:"); + + let x = BaseType; + + spec! { x.order_method(1u8); BaseType; [u8] } // -> "T is u8" + spec! { x.order_method(1i8); BaseType; [i8] } // -> "Default" +} + +struct BaseType; + +trait OrderTrait { + fn order_method(&self, _x: T); +} + +impl OrderTrait for BaseType { + fn order_method(&self, _x: T) { + println!("Default"); + } +} + +#[when(T = u8)] +impl OrderTrait for BaseType { + fn order_method(&self, _x: T) { + println!("T is u8"); + } +} diff --git a/spec-trait-impl/crates/spec-trait-bin/src/sself.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/sself.rs similarity index 98% rename from spec-trait-impl/crates/spec-trait-bin/src/sself.rs rename to spec-trait-impl/crates/spec-trait-bin/src/examples/sself.rs index 8ac8484..89404d0 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/sself.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/sself.rs @@ -60,7 +60,7 @@ impl SelfTrait for (U, V) { } pub fn run() { - println!("\n- Self Cases:"); + println!("\n- Self Examples:"); let x = BaseType1; spec! { x.self_method(42u8); BaseType1; [u8] } // -> "BaseType1: T is u8" diff --git a/spec-trait-impl/crates/spec-trait-bin/src/traits.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/traits.rs similarity index 97% rename from spec-trait-impl/crates/spec-trait-bin/src/traits.rs rename to spec-trait-impl/crates/spec-trait-bin/src/examples/traits.rs index 2495249..6719c33 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/traits.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/traits.rs @@ -48,7 +48,7 @@ impl TraitsTrait for BaseType { } pub fn run() { - println!("\n- Traits Cases:"); + println!("\n- Traits Examples:"); let x = BaseType; diff --git a/spec-trait-impl/crates/spec-trait-bin/src/types.rs b/spec-trait-impl/crates/spec-trait-bin/src/examples/types.rs similarity index 98% rename from spec-trait-impl/crates/spec-trait-bin/src/types.rs rename to spec-trait-impl/crates/spec-trait-bin/src/examples/types.rs index ed1ec75..b314cb5 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/types.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/examples/types.rs @@ -65,7 +65,7 @@ impl TypesTrait for BaseType { } pub fn run() { - println!("\n- Types Cases:"); + println!("\n- Types Examples:"); let x = BaseType; diff --git a/spec-trait-impl/crates/spec-trait-bin/src/main.rs b/spec-trait-impl/crates/spec-trait-bin/src/main.rs index 822490c..fb1c850 100644 --- a/spec-trait-impl/crates/spec-trait-bin/src/main.rs +++ b/spec-trait-impl/crates/spec-trait-bin/src/main.rs @@ -1,17 +1,5 @@ -mod all; -mod base; -mod lifetimes; -mod nested; -mod sself; -mod traits; -mod types; +mod examples; fn main() { - base::run(); - types::run(); - traits::run(); - lifetimes::run(); - nested::run(); - sself::run(); - all::run(); + examples::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 356685b..ee21b73 100644 --- a/spec-trait-impl/crates/spec-trait-utils/src/specialize.rs +++ b/spec-trait-impl/crates/spec-trait-utils/src/specialize.rs @@ -336,6 +336,18 @@ mod tests { ); } + #[test] + fn get_assignable_conditions_nested_types() { + let conditions = vec![ + WhenCondition::Type("T".into(), "A".into()), + WhenCondition::Type("U".into(), "for <'a> fn(&'a T) -> T".into()), + ]; + + let res = get_assignable_conditions(&conditions, ""); + + assert_eq!(res.len(), 2); + } + #[test] fn get_generic_types_from_conditions_ordering_and_filtering() { let conditions = vec![ diff --git a/spec-trait-impl/crates/spec-trait-utils/src/types.rs b/spec-trait-impl/crates/spec-trait-utils/src/types.rs index 52e01a5..1276b9f 100644 --- a/spec-trait-impl/crates/spec-trait-utils/src/types.rs +++ b/spec-trait-impl/crates/spec-trait-utils/src/types.rs @@ -164,7 +164,7 @@ fn can_assign( || to_string(&array1.len) == to_string(&array2.len)) } - // fn(T) -> U + // `fn(T) -> U`, `for<'a> fn(&'a T) -> U` (Type::BareFn(fn1), Type::BareFn(fn2)) => { let unsafety_compatible = matches!( (&fn1.unsafety, &fn2.unsafety), @@ -177,6 +177,26 @@ fn can_assign( _ => false, }; + // extend generics with the generics from the fn: `for<'a> fn(&'a T) -> U` + let all_new_lifetimes = fn1 + .lifetimes + .as_ref() + .map(|bl| &bl.lifetimes) + .iter() + .chain(fn2.lifetimes.as_ref().map(|bl| &bl.lifetimes).iter()) + .flat_map(|lts| { + lts.iter().filter_map(|gp| { + if let GenericParam::Lifetime(lt) = gp { + Some((lt.lifetime.to_string(), None)) + } else { + None + } + }) + }) + .collect::(); + + generics.lifetimes.extend(all_new_lifetimes); + fn1.inputs.len() == fn2.inputs.len() && unsafety_compatible && abi_compatible @@ -289,11 +309,12 @@ fn check_and_assign_type_generic( .cloned() .is_some_and(|assigned| { assigned.clone().is_none_or(|assigned| { - can_assign( - &str_to_type_name(concrete_type), - &str_to_type_name(&assigned), - generics, - ) + declared_type == assigned + || can_assign( + &str_to_type_name(concrete_type), + &str_to_type_name(&assigned), + generics, + ) }) }) { @@ -961,6 +982,48 @@ mod tests { assert!(!can_assign(&t1, &t2, &mut g)); } + #[test] + fn compare_types_generic_fn() { + let mut g = ConstrainedGenerics::default(); + + let t1 = str_to_type_name("for<'a> fn(&'a u8) -> i32"); + let t2 = str_to_type_name("for<'a> fn(&'a u8) -> i32"); + assert!(can_assign(&t1, &t2, &mut g)); + + g = ConstrainedGenerics::default(); + let t1 = str_to_type_name("for<'a> fn(&'a u8) -> i32"); + let t2 = str_to_type_name("for<'a> fn(&'a _) -> _"); + assert!(can_assign(&t1, &t2, &mut g)); + + g = ConstrainedGenerics::default(); + g.types.insert("T".to_string(), None); + let t1 = str_to_type_name("for<'a> fn(&'a u8) -> i32"); + let t2 = str_to_type_name("for<'a> fn(&'a T) -> i32"); + assert!(can_assign(&t1, &t2, &mut g)); + + g = ConstrainedGenerics::default(); + g.types.insert("T".to_string(), None); + let t1 = str_to_type_name("for<'a> fn(&'a u8) -> i32"); + let t2 = str_to_type_name("for<'a> fn(&'a u8) -> T"); + assert!(can_assign(&t1, &t2, &mut g)); + + g = ConstrainedGenerics::default(); + g.types.insert("T".to_string(), None); + let t1 = str_to_type_name("for<'a> fn(&'a u8) -> i32"); + let t2 = str_to_type_name("for<'a> fn(&'a T) -> T"); + assert!(!can_assign(&t1, &t2, &mut g)); + + g = ConstrainedGenerics::default(); + let t1 = str_to_type_name("for<'a> fn(&'a u8) -> i32"); + let t2 = str_to_type_name("for<'a> fn(&'a i32) -> i32"); + assert!(!can_assign(&t1, &t2, &mut g)); + + g = ConstrainedGenerics::default(); + let t1 = str_to_type_name("for<'a> fn(&'a u8) -> i32"); + let t2 = str_to_type_name("for<'a> fn(&'a u8) -> u8"); + assert!(!can_assign(&t1, &t2, &mut g)); + } + #[test] fn compare_types_nested() { let mut g = ConstrainedGenerics::default();