IntoPyObject for #[pyo3(get)]#4449
Conversation
|
Hmm, not sure if there is a way to rewrite pub trait PyO3GetFieldIntoPyObject: for<'py> IntoPyObject<'py> + Clone
where
for<'py> <Self as IntoPyObject<'py>>::Error: Into<PyErr>,
{
}but this forces additional trait bounds on the usage site and causes the diagnostic of |
davidhewitt
left a comment
There was a problem hiding this comment.
Nice, thanks!
Hmm, not sure if there is a way to rewrite PyO3GetFieldIntoPyObject without associated type bounds
Hmm, I had a quick go just now but no obvious success, will continue to explore another rmoment.
| let _holder = unsafe { | ||
| BoundRef::ref_from_ptr(py, &obj) | ||
| .downcast_unchecked::<ClassT>() | ||
| .try_borrow()? | ||
| }; |
There was a problem hiding this comment.
I think we now have a lot of repetition of this pattern, maybe refactor into a function? Possibly try of the offset operation just below also.
|
You can try using |
Given that |
I'm not particularly interested in putting in effort to prettify errors on old compilers. I would prefer having a simpler implementation. |
I think we mean the same thing, though the problem is that the trait exists to emit a diagnostic specific to this case! |
This could work, luckily associated type bounds were stabilized in 1.79 which is also the version we use for |
davidhewitt
left a comment
There was a problem hiding this comment.
Overall this is looking good; just some final thoughts to refine this one off! 👀
| where | ||
| ClassT: PyClass, | ||
| Offset: OffsetCalculator<ClassT, FieldT>, | ||
| FieldT: PyO3GetField, |
There was a problem hiding this comment.
I think there might be no need for this PyO3GetField trait any more, as it will never be used for a diagnostic? Might be able to simplify here.
| FieldT: PyO3GetField, | |
| FieldT: IntoPy<Py<PyAny>> + Clone |
There was a problem hiding this comment.
It might show if only IntoPy<PyObject> (and not IntoPyObject) is implemented while Clone is missing. But this is probably such a specific case (and we want people to use IntoPyObject anyway) that we should probably just remove it.
| // The bound goes here rather than on the block so that this impl is always available | ||
| // if no specialization is used instead | ||
| where | ||
| FieldT: PyO3GetFieldIntoPyObject, |
There was a problem hiding this comment.
Maybe if the other PyO3GetField trait can be removed, this one could be renamed to just PyO3GetField instead.
| PyMethodDefType::Getter(PyGetterDef { | ||
| name, | ||
| meth: pyo3_get_value_into_pyobject::<ClassT, FieldT, Offset>, | ||
| doc, | ||
| }) |
There was a problem hiding this comment.
Because there is a separate IntoPyObject + Clone case, this bound can never be satisfied, right?
| PyMethodDefType::Getter(PyGetterDef { | |
| name, | |
| meth: pyo3_get_value_into_pyobject::<ClassT, FieldT, Offset>, | |
| doc, | |
| }) | |
| unreachable!("exists purely to emit diagnostics on unimplemented traits") |
If so, I guess the same applies to the other cfg-d variant just above too.
There was a problem hiding this comment.
This is true until we can remove the ToPyObject and IntoPy cases, at which point we can use this case, and remove the special case for IntoPyObject. If you prefer, we can put unreachable until then.
There was a problem hiding this comment.
Actually I had to put panic since unreachable with a message is not allowed in const yet.
| = note: implement `ToPyObject` or `IntoPy<PyObject> + Clone` for `PhantomData<i32>` to define the conversion | ||
| = note: required for `PhantomData<i32>` to implement `PyO3GetField` | ||
| note: required by a bound in `PyClassGetterGenerator::<ClassT, FieldT, Offset, false, false>::generate` | ||
| = help: the trait `for<'py> IntoPyObject<'py>` is not implemented for `PhantomData<i32>`, which is required by `PhantomData<i32>: PyO3GetFieldIntoPyObject` |
There was a problem hiding this comment.
A thought: from testing locally, if I change the definition of PyO3GetFieldIntoPyObject to have a lifetime 'py, i.e. PyO3GetFieldIntoPyObject<'py>: IntoPyObject<'py> + Clone.
... then (after adjusting other uses accordingly), the error message looks more like this:
| = help: the trait `for<'py> IntoPyObject<'py>` is not implemented for `PhantomData<i32>`, which is required by `PhantomData<i32>: PyO3GetFieldIntoPyObject` | |
| = help: the trait `IntoPyObject<'_>` is not implemented for `PhantomData<i32>`, which is required by `for<'py> PhantomData<i32>: PyO3GetFieldIntoPyObject<'py>` |
Is that better? I think it might be slightly because it moves the HRTB later in the message. I think it's functionally equivalent otherwise?
There was a problem hiding this comment.
I think it should be equivalent to put the HRTB in the method bound instead of the the super trait, yes.
davidhewitt
left a comment
There was a problem hiding this comment.
Thanks, this one looks great to me now!
This extends the
#[pyo3(get)]conversion specialization to includeIntoPyObject. I implemented the following priority list (or at least tried to 🙃 )Py<T>IntoPyObjectfor&TIntoPyObject + CloneforT(to prefer overIntoPy)ToPyObjectforTIntoPy + CloneforTIntoPyObject + CloneforT(currently only used to emit the correct diagnostic if no conversion trait is implemented)