From f588ead1360bba6cf7ac0c4b72685d9c60817910 Mon Sep 17 00:00:00 2001 From: Jingyu Ma Date: Sun, 15 Mar 2026 21:35:18 -0700 Subject: [PATCH] Fix #107: support generic functions with reference type parameters in func! Remove type coercion in func! Case 1 and Case 2. Instead of coercing the function expression to the user-specified fn pointer type (which fails when a generic type parameter is a reference due to Rust's higher-ranked lifetime mismatch), cast the function item directly to *const () and use type_name::() for the signature string. For generic functions like execvp>, users can now use turbofish to monomorphize: func!(execvp::<&CStr>, fn(&CStr, &[&CStr]) -> Result<...>) This is non-breaking: all existing tests pass unchanged. --- src/interface/macros.rs | 10 ++++------ tests/will_execute.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/interface/macros.rs b/src/interface/macros.rs index b4ba1e5..8c11f95 100644 --- a/src/interface/macros.rs +++ b/src/interface/macros.rs @@ -19,9 +19,8 @@ macro_rules! func { // Case 1: Generic function — provide function name and types separately ($f:ident :: <$($gen:ty),*>, $fn_type:ty) => {{ - let fn_val:$fn_type = $f::<$($gen),*>; - let ptr = fn_val as *const (); - let sig = std::any::type_name_of_val(&fn_val); + let ptr = $f::<$($gen),*> as *const (); + let sig = std::any::type_name::<$fn_type>(); let type_id = std::any::TypeId::of::<$fn_type>(); unsafe { FuncPtr::new_with_type_id(ptr, sig, type_id) } @@ -29,9 +28,8 @@ macro_rules! func { // Case 2: Non-generic function with explicit type ($f:expr, $fn_type:ty) => {{ - let fn_val:$fn_type = $f; - let ptr = fn_val as *const (); - let sig = std::any::type_name_of_val(&fn_val); + let ptr = ($f) as *const (); + let sig = std::any::type_name::<$fn_type>(); let type_id = std::any::TypeId::of::<$fn_type>(); unsafe { FuncPtr::new_with_type_id(ptr, sig, type_id) } diff --git a/tests/will_execute.rs b/tests/will_execute.rs index 5b87398..53ab66b 100644 --- a/tests/will_execute.rs +++ b/tests/will_execute.rs @@ -572,3 +572,31 @@ fn test_will_execute_fake_unsafe_unit_assign_and_times_over_called_should_panic( }); assert!(result.is_err()); } + +// Generic function where type parameter S appears inside a slice reference. +// When monomorphized with S = &str, the fn pointer type has different lifetime +// counts than the explicit fn pointer type (issue #107). +fn generic_with_ref_type_param>( + _prefix: &str, + _items: &[S], +) -> String { + "original".to_string() +} + +#[test] +fn test_will_execute_generic_func_with_ref_type_param_via_turbofish() { + let mut injector = InjectorPP::new(); + injector + .when_called(injectorpp::func!( + generic_with_ref_type_param::<&str>, + fn(&str, &[&str]) -> String + )) + .will_execute(injectorpp::fake!( + func_type: fn(_prefix: &str, _items: &[&str]) -> String, + returns: "mocked".to_string(), + times: 1 + )); + + let result = generic_with_ref_type_param("hello", &["a", "b"]); + assert_eq!(result, "mocked"); +}