Skip to content
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 newsfragments/5998.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fixed compilation error for `#[new]` return types that contain named lifetimes
20 changes: 2 additions & 18 deletions pyo3-macros-backend/src/py_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -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> {
Expand Down
4 changes: 3 additions & 1 deletion pyo3-macros-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions pyo3-macros-backend/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
11 changes: 11 additions & 0 deletions src/tests/hygiene/pymethods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<'py, Self>> {
crate::Bound::new(py, Self)
}
}
Loading