From 5f20453264d237c984ca6e62aab88bee199befe0 Mon Sep 17 00:00:00 2001 From: Vasily Zorin Date: Thu, 18 Dec 2025 23:22:35 +0700 Subject: [PATCH] fix(macro): reference mutability inside Option #515 --- crates/macros/src/function.rs | 4 +++- tests/src/integration/array/array.php | 7 +++++++ tests/src/integration/array/mod.rs | 9 ++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/macros/src/function.rs b/crates/macros/src/function.rs index bcc4865cc..24af3307a 100644 --- a/crates/macros/src/function.rs +++ b/crates/macros/src/function.rs @@ -540,9 +540,11 @@ impl<'a> Args<'a> { .and_then(|ga| match ga { GenericArgument::Type(ty) => Some(match ty { Type::Reference(r) => { + // Only mark as_ref for mutable references + // (Option<&mut T>), not immutable ones (Option<&T>) + as_ref = r.mutability.is_some(); let mut new_ref = r.clone(); new_ref.mutability = None; - as_ref = true; Type::Reference(new_ref) } _ => ty.clone(), diff --git a/tests/src/integration/array/array.php b/tests/src/integration/array/array.php index 4969e3e9d..e8935b1f4 100644 --- a/tests/src/integration/array/array.php +++ b/tests/src/integration/array/array.php @@ -79,3 +79,10 @@ assert(array_key_exists('00', $leading_zeros), '"00" should stay as string key'); assert($leading_zeros['00'] === 'zerozero', 'Value at key "00" should be "zerozero"'); + +// Test Option<&ZendHashTable> with literal array (issue #515) +// This should work without "could not be passed by reference" error +assert(test_optional_array_ref([1, 2, 3]) === 3, 'Option<&ZendHashTable> should accept literal array'); +assert(test_optional_array_ref(null) === -1, 'Option<&ZendHashTable> should accept null'); +$arr = ['a', 'b', 'c', 'd']; +assert(test_optional_array_ref($arr) === 4, 'Option<&ZendHashTable> should accept variable array'); diff --git a/tests/src/integration/array/mod.rs b/tests/src/integration/array/mod.rs index b8922a176..6654b7eb5 100644 --- a/tests/src/integration/array/mod.rs +++ b/tests/src/integration/array/mod.rs @@ -5,7 +5,7 @@ use ext_php_rs::{ ffi::HashTable, php_function, prelude::ModuleBuilder, - types::{ArrayKey, Zval}, + types::{ArrayKey, ZendHashTable, Zval}, wrap_function, }; @@ -41,6 +41,12 @@ pub fn test_array_keys() -> Zval { ht.into_zval(false).unwrap() } +/// Test that `Option<&ZendHashTable>` can accept literal arrays (issue #515) +#[php_function] +pub fn test_optional_array_ref(arr: Option<&ZendHashTable>) -> i64 { + arr.map_or(-1, |ht| i64::try_from(ht.len()).unwrap_or(i64::MAX)) +} + pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { builder .function(wrap_function!(test_array)) @@ -48,6 +54,7 @@ pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { .function(wrap_function!(test_array_assoc_array_keys)) .function(wrap_function!(test_btree_map)) .function(wrap_function!(test_array_keys)) + .function(wrap_function!(test_optional_array_ref)) } #[cfg(test)]