From fca4b21fee9afb99671e57a6bfdcfc9a88f25e77 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Thu, 23 Apr 2026 19:11:38 +0200 Subject: [PATCH] fix error with named lifetimes in `#[new]` return types --- newsfragments/5998.fixed.md | 1 + pyo3-macros-backend/src/py_expr.rs | 20 ++------------------ pyo3-macros-backend/src/pymethod.rs | 4 +++- pyo3-macros-backend/src/utils.rs | 16 ++++++++++++++++ src/tests/hygiene/pymethods.rs | 11 +++++++++++ 5 files changed, 33 insertions(+), 19 deletions(-) create mode 100644 newsfragments/5998.fixed.md diff --git a/newsfragments/5998.fixed.md b/newsfragments/5998.fixed.md new file mode 100644 index 00000000000..4a98969369e --- /dev/null +++ b/newsfragments/5998.fixed.md @@ -0,0 +1 @@ +fixed compilation error for `#[new]` return types that contain named lifetimes diff --git a/pyo3-macros-backend/src/py_expr.rs b/pyo3-macros-backend/src/py_expr.rs index 44cfd089fab..d18556cc784 100644 --- a/pyo3-macros-backend/src/py_expr.rs +++ b/pyo3-macros-backend/src/py_expr.rs @@ -5,7 +5,7 @@ use proc_macro2::TokenStream; use quote::quote; use std::borrow::Cow; use syn::visit_mut::{visit_type_mut, VisitMut}; -use syn::{Expr, ExprLit, ExprPath, Lifetime, Lit, Type}; +use syn::{Expr, ExprLit, ExprPath, Lit, Type}; /// A Python expression /// @@ -268,26 +268,10 @@ fn clean_type(mut t: Type, self_type: Option<&Type>) -> Type { if let Some(self_type) = self_type { replace_self(&mut t, self_type); } - elide_lifetimes(&mut t); + crate::utils::elide_lifetimes(&mut t); t } -/// Replaces all explicit lifetimes in `self` with elided (`'_`) lifetimes -/// -/// This is useful if `Self` is used in `const` context, where explicit -/// lifetimes are not allowed (yet). -fn elide_lifetimes(ty: &mut Type) { - struct ElideLifetimesVisitor; - - impl VisitMut for ElideLifetimesVisitor { - fn visit_lifetime_mut(&mut self, l: &mut Lifetime) { - *l = Lifetime::new("'_", l.span()); - } - } - - ElideLifetimesVisitor.visit_type_mut(ty); -} - // Replace Self in types with the given type fn replace_self(ty: &mut Type, self_target: &Type) { struct SelfReplacementVisitor<'a> { diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 7748d239245..d82bc2241bc 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -1474,9 +1474,11 @@ fn generate_method_body( }); let output = if let syn::ReturnType::Type(_, ty) = &spec.output { + let mut ty = ty.clone(); + utils::elide_lifetimes(&mut ty); ty } else { - &parse_quote!(()) + parse_quote!(()) }; let body = quote! { #text_signature_impl diff --git a/pyo3-macros-backend/src/utils.rs b/pyo3-macros-backend/src/utils.rs index fa0dd90664b..56e07aaf8d7 100644 --- a/pyo3-macros-backend/src/utils.rs +++ b/pyo3-macros-backend/src/utils.rs @@ -375,3 +375,19 @@ pub(crate) fn locate_tokens_at(tokens: TokenStream, span: Span) -> TokenStream { let output_span = tokens.span().located_at(span); set_span_recursively(tokens, output_span) } + +/// Replaces all explicit lifetimes in `self` with elided (`'_`) lifetimes +/// +/// This is useful if `Self` is used in `const` context, where explicit +/// lifetimes are not allowed (yet). +pub(crate) fn elide_lifetimes(ty: &mut syn::Type) { + struct ElideLifetimesVisitor; + + impl syn::visit_mut::VisitMut for ElideLifetimesVisitor { + fn visit_lifetime_mut(&mut self, l: &mut syn::Lifetime) { + *l = syn::Lifetime::new("'_", l.span()); + } + } + + syn::visit_mut::VisitMut::visit_type_mut(&mut ElideLifetimesVisitor, ty); +} diff --git a/src/tests/hygiene/pymethods.rs b/src/tests/hygiene/pymethods.rs index 87babfac220..002af4b26cd 100644 --- a/src/tests/hygiene/pymethods.rs +++ b/src/tests/hygiene/pymethods.rs @@ -566,3 +566,14 @@ impl WarningDummy2 { #[pyo3(warn(message = "this class-method raises user-defined warning", category = UserDefinedWarning))] fn multiple_warnings_fn_with_custom_category(&self) {} } + +#[crate::pyclass(crate = "crate")] +struct NewReturnsNamedLifetime; + +#[crate::pymethods(crate = "crate")] +impl NewReturnsNamedLifetime { + #[new] + fn new<'py>(py: crate::Python<'py>) -> crate::PyResult> { + crate::Bound::new(py, Self) + } +}