diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index cb025149..7050be23 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -103,12 +103,18 @@ impl FnType { } } - pub fn self_arg(&self, cls: Option<&syn::Type>, error_mode: ExtractErrorMode) -> TokenStream { + pub fn self_arg( + &self, + cls: Option<&syn::Type>, + error_mode: ExtractErrorMode, + holders: &mut Vec, + ) -> TokenStream { match self { FnType::Getter(st) | FnType::Setter(st) | FnType::Fn(st) => { let mut receiver = st.receiver( cls.expect("no class given for Fn with a \"self\" receiver"), error_mode, + holders, ); syn::Token![,](Span::call_site()).to_tokens(&mut receiver); receiver @@ -161,7 +167,12 @@ impl ExtractErrorMode { } impl SelfType { - pub fn receiver(&self, cls: &syn::Type, error_mode: ExtractErrorMode) -> TokenStream { + pub fn receiver( + &self, + cls: &syn::Type, + error_mode: ExtractErrorMode, + holders: &mut Vec, + ) -> TokenStream { // Due to use of quote_spanned in this function, need to bind these idents to the // main macro callsite. let py = syn::Ident::new("py", Span::call_site()); @@ -173,10 +184,15 @@ impl SelfType { } else { syn::Ident::new("extract_pyclass_ref", *span) }; + let holder = syn::Ident::new(&format!("holder_{}", holders.len()), *span); + holders.push(quote_spanned! { *span => + #[allow(clippy::let_unit_value)] + let mut #holder = _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT; + }); error_mode.handle_error(quote_spanned! { *span => _pyo3::impl_::extract_argument::#method::<#cls>( #py.from_borrowed_ptr::<_pyo3::PyAny>(#slf), - &mut { _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT }, + &mut #holder, ) }) } @@ -457,9 +473,6 @@ impl<'a> FnSpec<'a> { ident: &proc_macro2::Ident, cls: Option<&syn::Type>, ) -> Result { - let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise); - let func_name = &self.name; - let mut cancel_handle_iter = self .signature .arguments @@ -473,7 +486,9 @@ impl<'a> FnSpec<'a> { } } - let rust_call = |args: Vec| { + let rust_call = |args: Vec, holders: &mut Vec| { + let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise, holders); + let call = if self.asyncness.is_some() { let throw_callback = if cancel_handle.is_some() { quote! { Some(__throw_callback) } @@ -486,14 +501,22 @@ impl<'a> FnSpec<'a> { None => quote!(None), }; let future = match self.tp { - FnType::Fn(SelfType::Receiver { mutable: false, .. }) => quote! {{ - let __guard = _pyo3::impl_::coroutine::RefGuard::<#cls>::new(py.from_borrowed_ptr::<_pyo3::types::PyAny>(_slf))?; - async move { function(&__guard, #(#args),*).await } - }}, - FnType::Fn(SelfType::Receiver { mutable: true, .. }) => quote! {{ - let mut __guard = _pyo3::impl_::coroutine::RefMutGuard::<#cls>::new(py.from_borrowed_ptr::<_pyo3::types::PyAny>(_slf))?; - async move { function(&mut __guard, #(#args),*).await } - }}, + FnType::Fn(SelfType::Receiver { mutable: false, .. }) => { + holders.pop().unwrap(); // does not actually use holder created by `self_arg` + + quote! {{ + let __guard = _pyo3::impl_::coroutine::RefGuard::<#cls>::new(py.from_borrowed_ptr::<_pyo3::types::PyAny>(_slf))?; + async move { function(&__guard, #(#args),*).await } + }} + } + FnType::Fn(SelfType::Receiver { mutable: true, .. }) => { + holders.pop().unwrap(); // does not actually use holder created by `self_arg` + + quote! {{ + let mut __guard = _pyo3::impl_::coroutine::RefMutGuard::<#cls>::new(py.from_borrowed_ptr::<_pyo3::types::PyAny>(_slf))?; + async move { function(&mut __guard, #(#args),*).await } + }} + } _ => quote! { function(#self_arg #(#args),*) }, }; let mut call = quote! {{ @@ -519,6 +542,7 @@ impl<'a> FnSpec<'a> { quotes::map_result_into_ptr(quotes::ok_wrap(call)) }; + let func_name = &self.name; let rust_name = if let Some(cls) = cls { quote!(#cls::#func_name) } else { @@ -527,6 +551,7 @@ impl<'a> FnSpec<'a> { Ok(match self.convention { CallingConvention::Noargs => { + let mut holders = Vec::new(); let args = self .signature .arguments @@ -541,7 +566,7 @@ impl<'a> FnSpec<'a> { } }) .collect(); - let call = rust_call(args); + let call = rust_call(args, &mut holders); quote! { unsafe fn #ident<'py>( @@ -549,13 +574,15 @@ impl<'a> FnSpec<'a> { _slf: *mut _pyo3::ffi::PyObject, ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { let function = #rust_name; // Shadow the function name to avoid #3017 + #( #holders )* #call } } } CallingConvention::Fastcall => { - let (arg_convert, args) = impl_arg_params(self, cls, true)?; - let call = rust_call(args); + let mut holders = Vec::new(); + let (arg_convert, args) = impl_arg_params(self, cls, true, &mut holders)?; + let call = rust_call(args, &mut holders); quote! { unsafe fn #ident<'py>( py: _pyo3::Python<'py>, @@ -566,13 +593,15 @@ impl<'a> FnSpec<'a> { ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { let function = #rust_name; // Shadow the function name to avoid #3017 #arg_convert + #( #holders )* #call } } } CallingConvention::Varargs => { - let (arg_convert, args) = impl_arg_params(self, cls, false)?; - let call = rust_call(args); + let mut holders = Vec::new(); + let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders)?; + let call = rust_call(args, &mut holders); quote! { unsafe fn #ident<'py>( py: _pyo3::Python<'py>, @@ -582,13 +611,15 @@ impl<'a> FnSpec<'a> { ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { let function = #rust_name; // Shadow the function name to avoid #3017 #arg_convert + #( #holders )* #call } } } CallingConvention::TpNew => { - let (arg_convert, args) = impl_arg_params(self, cls, false)?; - let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise); + let mut holders = Vec::new(); + let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders)?; + let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise, &mut holders); let call = quote! { #rust_name(#self_arg #(#args),*) }; quote! { unsafe fn #ident( @@ -600,6 +631,7 @@ impl<'a> FnSpec<'a> { use _pyo3::callback::IntoPyCallbackOutput; let function = #rust_name; // Shadow the function name to avoid #3017 #arg_convert + #( #holders )* let result = #call; let initializer: _pyo3::PyClassInitializer::<#cls> = result.convert(py)?; let cell = initializer.create_cell_from_subtype(py, _slf)?; diff --git a/pyo3-macros-backend/src/params.rs b/pyo3-macros-backend/src/params.rs index d8a0dc74..1ef31867 100644 --- a/pyo3-macros-backend/src/params.rs +++ b/pyo3-macros-backend/src/params.rs @@ -29,24 +29,23 @@ pub fn impl_arg_params( spec: &FnSpec<'_>, self_: Option<&syn::Type>, fastcall: bool, + holders: &mut Vec, ) -> Result<(TokenStream, Vec)> { let args_array = syn::Ident::new("output", Span::call_site()); if !fastcall && is_forwarded_args(&spec.signature) { // In the varargs convention, we can just pass though if the signature // is (*args, **kwds). - let mut holders = Vec::new(); let arg_convert = spec .signature .arguments .iter() - .map(|arg| impl_arg_param(arg, &mut 0, &args_array, &mut holders)) + .map(|arg| impl_arg_param(arg, &mut 0, &args_array, holders)) .collect::>()?; return Ok(( quote! { let _args = py.from_borrowed_ptr::<_pyo3::types::PyTuple>(_args); let _kwargs: ::std::option::Option<&_pyo3::types::PyDict> = py.from_borrowed_ptr_or_opt(_kwargs); - #( #holders )* }, arg_convert, )); @@ -75,12 +74,11 @@ pub fn impl_arg_params( let num_params = positional_parameter_names.len() + keyword_only_parameters.len(); let mut option_pos = 0; - let mut holders = Vec::new(); let param_conversion = spec .signature .arguments .iter() - .map(|arg| impl_arg_param(arg, &mut option_pos, &args_array, &mut holders)) + .map(|arg| impl_arg_param(arg, &mut option_pos, &args_array, holders)) .collect::>()?; let args_handler = if spec.signature.python_signature.varargs.is_some() { @@ -134,7 +132,6 @@ pub fn impl_arg_params( keyword_only_parameters: &[#(#keyword_only_parameters),*], }; let mut #args_array = [::std::option::Option::None; #num_params]; - #( #holders )* let (_args, _kwargs) = #extract_expression; }, param_conversion, diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index b87f4644..239bd96a 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -481,9 +481,10 @@ fn impl_call_setter( cls: &syn::Type, spec: &FnSpec<'_>, self_type: &SelfType, + holders: &mut Vec, ) -> syn::Result { let (py_arg, args) = split_off_python_arg(&spec.signature.arguments); - let slf = self_type.receiver(cls, ExtractErrorMode::Raise); + let slf = self_type.receiver(cls, ExtractErrorMode::Raise, holders); if args.is_empty() { bail_spanned!(spec.name.span() => "setter function expected to have one argument"); @@ -511,6 +512,7 @@ pub fn impl_py_setter_def( ) -> Result { let python_name = property_type.null_terminated_python_name()?; let doc = property_type.doc(); + let mut holders = Vec::new(); let setter_impl = match property_type { PropertyType::Descriptor { field_index, field, .. @@ -519,7 +521,7 @@ pub fn impl_py_setter_def( mutable: true, span: Span::call_site(), } - .receiver(cls, ExtractErrorMode::Raise); + .receiver(cls, ExtractErrorMode::Raise, &mut holders); if let Some(ident) = &field.ident { // named struct field quote!({ #slf.#ident = _val; }) @@ -531,7 +533,7 @@ pub fn impl_py_setter_def( } PropertyType::Function { spec, self_type, .. - } => impl_call_setter(cls, spec, self_type)?, + } => impl_call_setter(cls, spec, self_type, &mut holders)?, }; let wrapper_ident = match property_type { @@ -575,7 +577,7 @@ pub fn impl_py_setter_def( _pyo3::exceptions::PyAttributeError::new_err("can't delete attribute") })?; let _val = _pyo3::FromPyObject::extract(_value)?; - + #( #holders )* _pyo3::callback::convert(py, #setter_impl) } }; @@ -601,9 +603,10 @@ fn impl_call_getter( cls: &syn::Type, spec: &FnSpec<'_>, self_type: &SelfType, + holders: &mut Vec, ) -> syn::Result { let (py_arg, args) = split_off_python_arg(&spec.signature.arguments); - let slf = self_type.receiver(cls, ExtractErrorMode::Raise); + let slf = self_type.receiver(cls, ExtractErrorMode::Raise, holders); ensure_spanned!( args.is_empty(), args[0].ty.span() => "getter function can only have one argument (of type pyo3::Python)" @@ -627,6 +630,7 @@ pub fn impl_py_getter_def( let python_name = property_type.null_terminated_python_name()?; let doc = property_type.doc(); + let mut holders = Vec::new(); let body = match property_type { PropertyType::Descriptor { field_index, field, .. @@ -635,7 +639,7 @@ pub fn impl_py_getter_def( mutable: false, span: Span::call_site(), } - .receiver(cls, ExtractErrorMode::Raise); + .receiver(cls, ExtractErrorMode::Raise, &mut holders); let field_token = if let Some(ident) = &field.ident { // named struct field ident.to_token_stream() @@ -651,7 +655,7 @@ pub fn impl_py_getter_def( PropertyType::Function { spec, self_type, .. } => { - let call = impl_call_getter(cls, spec, self_type)?; + let call = impl_call_getter(cls, spec, self_type, &mut holders)?; quote! { _pyo3::callback::convert(py, #call) } @@ -692,6 +696,7 @@ pub fn impl_py_getter_def( py: _pyo3::Python<'_>, _slf: *mut _pyo3::ffi::PyObject ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { + #( #holders )* #body } }; @@ -1154,7 +1159,7 @@ fn generate_method_body( holders: &mut Vec, return_mode: Option<&ReturnMode>, ) -> Result { - let self_arg = spec.tp.self_arg(Some(cls), extract_error_mode); + let self_arg = spec.tp.self_arg(Some(cls), extract_error_mode, holders); let rust_name = spec.name; let args = extract_proto_arguments(spec, arguments, extract_error_mode, holders)?; let call = quote! { _pyo3::callback::convert(py, #cls::#rust_name(#self_arg #(#args),*)) };