Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix compile failure for getter with return lifetime of self #3347

Merged
merged 3 commits into from
Jul 30, 2023
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
1 change: 1 addition & 0 deletions pyo3-macros-backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod pyclass;
mod pyfunction;
mod pyimpl;
mod pymethod;
mod quotes;

pub use frompyobject::build_derive_from_pyobject;
pub use module::{process_functions_in_module, pymodule_impl, PyModuleOptions};
Expand Down
15 changes: 6 additions & 9 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::attributes::{TextSignatureAttribute, TextSignatureAttributeValue};
use crate::params::impl_arg_params;
use crate::pyfunction::{FunctionSignature, PyFunctionArgPyO3Attributes};
use crate::pyfunction::{PyFunctionOptions, SignatureAttribute};
use crate::quotes;
use crate::utils::{self, PythonDoc};
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
Expand Down Expand Up @@ -115,12 +116,12 @@ impl FnType {
}
FnType::FnClass | FnType::FnNewClass => {
quote! {
_pyo3::types::PyType::from_type_ptr(_py, _slf as *mut _pyo3::ffi::PyTypeObject),
_pyo3::types::PyType::from_type_ptr(py, _slf as *mut _pyo3::ffi::PyTypeObject),
}
}
FnType::FnModule => {
quote! {
_py.from_borrowed_ptr::<_pyo3::types::PyModule>(_slf),
py.from_borrowed_ptr::<_pyo3::types::PyModule>(_slf),
}
}
}
Expand Down Expand Up @@ -155,7 +156,7 @@ impl ExtractErrorMode {

impl SelfType {
pub fn receiver(&self, cls: &syn::Type, error_mode: ExtractErrorMode) -> TokenStream {
let py = syn::Ident::new("_py", Span::call_site());
let py = syn::Ident::new("py", Span::call_site());
let slf = syn::Ident::new("_slf", Span::call_site());
match self {
SelfType::Receiver { span, mutable } => {
Expand Down Expand Up @@ -409,15 +410,11 @@ impl<'a> FnSpec<'a> {
cls: Option<&syn::Type>,
) -> Result<TokenStream> {
let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise);
let py = syn::Ident::new("_py", Span::call_site());
let py = syn::Ident::new("py", Span::call_site());
let func_name = &self.name;

let rust_call = |args: Vec<TokenStream>| {
quote! {
_pyo3::impl_::pymethods::OkWrap::wrap(function(#self_arg #(#args),*), #py)
.map(|obj| _pyo3::conversion::IntoPyPointer::into_ptr(obj))
.map_err(::core::convert::Into::into)
}
quotes::map_result_into_ptr(quotes::ok_wrap(quote! { function(#self_arg #(#args),*) }))
};

let rust_name = if let Some(cls) = cls {
Expand Down
2 changes: 1 addition & 1 deletion pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,7 @@ impl<'a> PyClassImplsBuilder<'a> {
quote! {
impl _pyo3::impl_::pyclass::PyClassWithFreeList for #cls {
#[inline]
fn get_free_list(_py: _pyo3::Python<'_>) -> &mut _pyo3::impl_::freelist::FreeList<*mut _pyo3::ffi::PyObject> {
fn get_free_list(py: _pyo3::Python<'_>) -> &mut _pyo3::impl_::freelist::FreeList<*mut _pyo3::ffi::PyObject> {
static mut FREELIST: *mut _pyo3::impl_::freelist::FreeList<*mut _pyo3::ffi::PyObject> = 0 as *mut _;
unsafe {
if FREELIST.is_null() {
Expand Down
56 changes: 24 additions & 32 deletions pyo3-macros-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ use std::borrow::Cow;

use crate::attributes::NameAttribute;
use crate::method::{CallingConvention, ExtractErrorMode};
use crate::utils;
use crate::utils::{ensure_not_async_fn, PythonDoc};
use crate::{
method::{FnArg, FnSpec, FnType, SelfType},
pyfunction::PyFunctionOptions,
};
use crate::{quotes, utils};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{ext::IdentExt, spanned::Spanned, Result};
Expand Down Expand Up @@ -451,12 +451,12 @@ fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<Me

let wrapper_ident = format_ident!("__pymethod_{}__", name);
let python_name = spec.null_terminated_python_name();
let body = quotes::ok_wrap(fncall);

let associated_method = quote! {
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
let function = #cls::#name; // Shadow the method name to avoid #3017
_pyo3::impl_::pymethods::OkWrap::wrap(#fncall, py)
.map_err(::core::convert::Into::into)
#body
}
};

Expand Down Expand Up @@ -494,7 +494,7 @@ fn impl_call_setter(

let name = &spec.name;
let fncall = if py_arg.is_some() {
quote!(#cls::#name(#slf, _py, _val))
quote!(#cls::#name(#slf, py, _val))
} else {
quote!(#cls::#name(#slf, _val))
};
Expand Down Expand Up @@ -563,18 +563,18 @@ pub fn impl_py_setter_def(
let associated_method = quote! {
#cfg_attrs
unsafe fn #wrapper_ident(
_py: _pyo3::Python<'_>,
py: _pyo3::Python<'_>,
_slf: *mut _pyo3::ffi::PyObject,
_value: *mut _pyo3::ffi::PyObject,
) -> _pyo3::PyResult<::std::os::raw::c_int> {
let _value = _py
let _value = py
.from_borrowed_ptr_or_opt(_value)
.ok_or_else(|| {
_pyo3::exceptions::PyAttributeError::new_err("can't delete attribute")
})?;
let _val = _pyo3::FromPyObject::extract(_value)?;

_pyo3::callback::convert(_py, #setter_impl)
_pyo3::callback::convert(py, #setter_impl)
}
};

Expand Down Expand Up @@ -609,7 +609,7 @@ fn impl_call_getter(

let name = &spec.name;
let fncall = if py_arg.is_some() {
quote!(#cls::#name(#slf, _py))
quote!(#cls::#name(#slf, py))
} else {
quote!(#cls::#name(#slf))
};
Expand All @@ -625,7 +625,7 @@ pub fn impl_py_getter_def(
let python_name = property_type.null_terminated_python_name()?;
let doc = property_type.doc();

let getter_impl = match property_type {
let body = match property_type {
PropertyType::Descriptor {
field_index, field, ..
} => {
Expand All @@ -634,31 +634,24 @@ pub fn impl_py_getter_def(
span: Span::call_site(),
}
.receiver(cls, ExtractErrorMode::Raise);
if let Some(ident) = &field.ident {
let field_token = if let Some(ident) = &field.ident {
// named struct field
quote!(::std::clone::Clone::clone(&(#slf.#ident)))
ident.to_token_stream()
} else {
// tuple struct field
let index = syn::Index::from(field_index);
quote!(::std::clone::Clone::clone(&(#slf.#index)))
}
syn::Index::from(field_index).to_token_stream()
};
quotes::map_result_into_ptr(quotes::ok_wrap(quote! {
::std::clone::Clone::clone(&(#slf.#field_token))
}))
}
// Forward to `IntoPyCallbackOutput`, to handle `#[getter]`s returning results.
PropertyType::Function {
spec, self_type, ..
} => impl_call_getter(cls, spec, self_type)?,
};

let conversion = match property_type {
PropertyType::Descriptor { .. } => {
quote! {
let item: _pyo3::Py<_pyo3::PyAny> = _pyo3::IntoPy::into_py(item, _py);
::std::result::Result::Ok(_pyo3::conversion::IntoPyPointer::into_ptr(item))
}
}
// Forward to `IntoPyCallbackOutput`, to handle `#[getter]`s returning results.
PropertyType::Function { .. } => {
} => {
let call = impl_call_getter(cls, spec, self_type)?;
quote! {
_pyo3::callback::convert(_py, item)
_pyo3::callback::convert(py, #call)
}
}
};
Expand Down Expand Up @@ -694,11 +687,10 @@ pub fn impl_py_getter_def(
let associated_method = quote! {
#cfg_attrs
unsafe fn #wrapper_ident(
_py: _pyo3::Python<'_>,
py: _pyo3::Python<'_>,
_slf: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
let item = #getter_impl;
#conversion
#body
}
};

Expand Down Expand Up @@ -1088,7 +1080,7 @@ impl SlotDef {
spec.name.span() => format!("`{}` must be `unsafe fn`", method_name)
);
}
let py = syn::Ident::new("_py", Span::call_site());
let py = syn::Ident::new("py", Span::call_site());
let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type()).collect();
let arg_idents: &Vec<_> = &(0..arguments.len())
.map(|i| format_ident!("arg{}", i))
Expand Down Expand Up @@ -1196,7 +1188,7 @@ impl SlotFragmentDef {
let fragment_trait = format_ident!("PyClass{}SlotFragment", fragment);
let method = syn::Ident::new(fragment, Span::call_site());
let wrapper_ident = format_ident!("__pymethod_{}__", fragment);
let py = syn::Ident::new("_py", Span::call_site());
let py = syn::Ident::new("py", Span::call_site());
let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type()).collect();
let arg_idents: &Vec<_> = &(0..arguments.len())
.map(|i| format_ident!("arg{}", i))
Expand Down
15 changes: 15 additions & 0 deletions pyo3-macros-backend/src/quotes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use proc_macro2::TokenStream;
use quote::quote;

pub(crate) fn ok_wrap(obj: TokenStream) -> TokenStream {
quote! {
_pyo3::impl_::pymethods::OkWrap::wrap(#obj, py)
.map_err(::core::convert::Into::into)
}
}

pub(crate) fn map_result_into_ptr(result: TokenStream) -> TokenStream {
quote! {
#result.map(_pyo3::IntoPyPointer::into_ptr)
}
}
20 changes: 20 additions & 0 deletions tests/test_getter_setter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,23 @@ fn cell_getter_setter() {
);
});
}

#[test]
fn borrowed_value_with_lifetime_of_self() {
#[pyclass]
struct BorrowedValue {}

#[pymethods]
impl BorrowedValue {
#[getter]
fn value(&self) -> &str {
"value"
}
}

Python::with_gil(|py| {
let inst = Py::new(py, BorrowedValue {}).unwrap().to_object(py);

py_run!(py, inst, "assert inst.value == 'value'");
});
}