Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ impl<T> Foo4 for Vec<T> {
}

pub fn run() {
println!("\n- All Cases:");
println!("\n- All Examples:");

let zst = ZST;
let zst2 = ZST2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,22 @@ impl<T, U> BaseTrait<T> for BaseType {
}
}

#[when(not(T = &BaseType))]
impl<T> BaseTrait<T> 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;

spec! { x.base_method(42u8); BaseType; [u8] } // T is u8
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>]; 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
}
33 changes: 33 additions & 0 deletions spec-trait-impl/crates/spec-trait-bin/src/examples/higher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use spec_trait_macro::{spec, when};

struct BaseType;

#[allow(dead_code)]
trait HigherOrderSpecTrait<F, T> {
fn higher_order_spec_method(&self, f: F, arg: T) -> T;
}

#[when(F = for<'a> fn(&'a T) -> T)]
impl<F, T> HigherOrderSpecTrait<F, T> 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<F, T> HigherOrderSpecTrait<F, T> 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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl<T> LifetimesTrait<T> for BaseType {
}

pub fn run() {
println!("\n- Lifetimes Cases:");
println!("\n- Lifetimes Examples:");

let x = BaseType;

Expand Down
21 changes: 21 additions & 0 deletions spec-trait-impl/crates/spec-trait-bin/src/examples/mod.rs
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl<T, U> NestedTrait<T, U> for BaseType {
}

pub fn run() {
println!("\n- Nested Cases:");
println!("\n- Nested Examples:");

let x = BaseType;

Expand Down
29 changes: 29 additions & 0 deletions spec-trait-impl/crates/spec-trait-bin/src/examples/order.rs
Original file line number Diff line number Diff line change
@@ -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<T> {
fn order_method(&self, _x: T);
}

impl<T> OrderTrait<T> for BaseType {
fn order_method(&self, _x: T) {
println!("Default");
}
}

#[when(T = u8)]
impl<T> OrderTrait<T> for BaseType {
fn order_method(&self, _x: T) {
println!("T is u8");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl<T, U, V> SelfTrait<T> 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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl<T> TraitsTrait<T> for BaseType {
}

pub fn run() {
println!("\n- Traits Cases:");
println!("\n- Traits Examples:");

let x = BaseType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl<T> TypesTrait<T> for BaseType {
}

pub fn run() {
println!("\n- Types Cases:");
println!("\n- Types Examples:");

let x = BaseType;

Expand Down
16 changes: 2 additions & 14 deletions spec-trait-impl/crates/spec-trait-bin/src/main.rs
Original file line number Diff line number Diff line change
@@ -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();
}
12 changes: 12 additions & 0 deletions spec-trait-impl/crates/spec-trait-utils/src/specialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, "<T, U>");

assert_eq!(res.len(), 2);
}

#[test]
fn get_generic_types_from_conditions_ordering_and_filtering() {
let conditions = vec![
Expand Down
75 changes: 69 additions & 6 deletions spec-trait-impl/crates/spec-trait-utils/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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::<GenericsMap>();

generics.lifetimes.extend(all_new_lifetimes);

fn1.inputs.len() == fn2.inputs.len()
&& unsafety_compatible
&& abi_compatible
Expand Down Expand Up @@ -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,
)
})
})
{
Expand Down Expand Up @@ -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();
Expand Down