Skip to content

Commit

Permalink
Fix ambiguous associated item error (#4725)
Browse files Browse the repository at this point in the history
* Add test for ambiguous associated item

The `#[pyclass]` macro implements `IntoPyObject` for the annotated enum.
When the enum has any of `Error`, `Output` or `Target` as members, this
clashes with the associated types of `IntoPyObject`.

This also happens when deriving `IntoPyObject` directly.

* Fix #4723: ambiguous associated item in #[pyclass]

This uses the fix described in rust-lang/rust#57644

Also apply fix to `#[derive(IntoPyObject)]`.
  • Loading branch information
LilyFoote authored and davidhewitt committed Nov 25, 2024
1 parent ab999ae commit 02eb2a1
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 6 deletions.
1 change: 1 addition & 0 deletions newsfragments/4725.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `ambiguous_associated_items` lint error in `#[pyclass]` and `#[derive(IntoPyObject)]` macros.
7 changes: 5 additions & 2 deletions pyo3-macros-backend/src/intopyobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ impl<'a> Enum<'a> {
IntoPyObjectImpl {
types: IntoPyObjectTypes::Opaque {
target: quote!(#pyo3_path::types::PyAny),
output: quote!(#pyo3_path::Bound<'py, Self::Target>),
output: quote!(#pyo3_path::Bound<'py, <Self as #pyo3_path::conversion::IntoPyObject<'py>>::Target>),
error: quote!(#pyo3_path::PyErr),
},
body: quote! {
Expand Down Expand Up @@ -617,7 +617,10 @@ pub fn build_derive_into_pyobject<const REF: bool>(tokens: &DeriveInput) -> Resu
type Output = #output;
type Error = #error;

fn into_pyobject(self, py: #pyo3_path::Python<#lt_param>) -> ::std::result::Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: #pyo3_path::Python<#lt_param>) -> ::std::result::Result<
<Self as #pyo3_path::conversion::IntoPyObject>::Output,
<Self as #pyo3_path::conversion::IntoPyObject>::Error,
> {
#body
}
}
Expand Down
14 changes: 10 additions & 4 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1072,10 +1072,13 @@ fn impl_complex_enum(
quote! {
impl<'py> #pyo3_path::conversion::IntoPyObject<'py> for #cls {
type Target = Self;
type Output = #pyo3_path::Bound<'py, Self::Target>;
type Output = #pyo3_path::Bound<'py, <Self as #pyo3_path::conversion::IntoPyObject<'py>>::Target>;
type Error = #pyo3_path::PyErr;

fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> ::std::result::Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> ::std::result::Result<
<Self as #pyo3_path::conversion::IntoPyObject>::Output,
<Self as #pyo3_path::conversion::IntoPyObject>::Error,
> {
match self {
#(#match_arms)*
}
Expand Down Expand Up @@ -2161,10 +2164,13 @@ impl<'a> PyClassImplsBuilder<'a> {

impl<'py> #pyo3_path::conversion::IntoPyObject<'py> for #cls {
type Target = Self;
type Output = #pyo3_path::Bound<'py, Self::Target>;
type Output = #pyo3_path::Bound<'py, <Self as #pyo3_path::conversion::IntoPyObject<'py>>::Target>;
type Error = #pyo3_path::PyErr;

fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> ::std::result::Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> ::std::result::Result<
<Self as #pyo3_path::conversion::IntoPyObject>::Output,
<Self as #pyo3_path::conversion::IntoPyObject>::Error,
> {
#pyo3_path::Bound::new(py, self)
}
}
Expand Down
1 change: 1 addition & 0 deletions tests/test_compile_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,5 @@ fn test_compile_errors() {
t.compile_fail("tests/ui/duplicate_pymodule_submodule.rs");
#[cfg(all(not(Py_LIMITED_API), Py_3_11))]
t.compile_fail("tests/ui/invalid_base_class.rs");
t.pass("tests/ui/ambiguous_associated_items.rs");
}
25 changes: 25 additions & 0 deletions tests/ui/ambiguous_associated_items.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use pyo3::prelude::*;

#[pyclass(eq)]
#[derive(PartialEq)]
pub enum SimpleItems {
Error,
Output,
Target,
}

#[pyclass]
pub enum ComplexItems {
Error(PyObject),
Output(PyObject),
Target(PyObject),
}

#[derive(IntoPyObject)]
enum DeriveItems {
Error(PyObject),
Output(PyObject),
Target(PyObject),
}

fn main() {}

0 comments on commit 02eb2a1

Please sign in to comment.