Skip to content

Commit e12b9e2

Browse files
alexclaude
andcommitted
Fix UI test failures from slimmed codegen
Three codegen bugs plus stale UI test snapshots: - The fused `unsafe` extraction blocks (extract_required_argument, from_py_with_required, extract_receiver{,_trusted}) were emitted inside quote_spanned! with user-code spans, so the `unsafe` token was attributed to user code and rejected by #![forbid(unsafe_code)]. Build the unsafe wrapper with call-site spans and interpolate the spanned call into it, as the previous codegen did. - `fn into_pyobject(...) -> PyResult<Self::Output>` is ambiguous for enums with a variant named `Output`; spell out the concrete return type `PyResult<Bound<'py, Self>>` instead. - Re-bless the UI test snapshots for the renamed extraction/conversion helpers, and update inline annotations: - invalid_pymethod_receiver now uses a local receiver type, because the fused receiver extraction produces a `From` candidate list for `i32` which varies with enabled features (jiff impls under `full`) - missing_intopy no longer emits the `map_err` error (the old ok_wrap + map_result_into_ptr codegen is gone) - invalid_pyclass_generic gains never_type_fallback diagnostics from the unsafe fused helpers in already-erroneous code Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent c7f7410 commit e12b9e2

16 files changed

Lines changed: 224 additions & 223 deletions

pyo3-macros-backend/src/method.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -482,18 +482,19 @@ impl SelfType {
482482
// type); on PyPy under `ExtractErrorMode::NotImplemented` they now
483483
// fall back to the default like `TryFrom` failures always have,
484484
// instead of raising directly.
485-
quote_spanned! { *span =>
486-
unsafe {
487-
#pyo3_path::impl_::extract_argument::extract_receiver_trusted::<#cls, _>(#bound_ref)
488-
}
489-
}
485+
// The `unsafe` token is deliberately spanned at the macro call site
486+
// (plain `quote!`) so that `#![forbid(unsafe_code)]` in user code
487+
// doesn't reject the generated unsafe block.
488+
let call = quote_spanned! { *span =>
489+
#pyo3_path::impl_::extract_argument::extract_receiver_trusted::<#cls, _>(#bound_ref)
490+
};
491+
quote! { unsafe { #call } }
490492
}
491493
SelfConversionPolicyInner::Checked => {
492-
quote_spanned! { *span =>
493-
unsafe {
494-
#pyo3_path::impl_::extract_argument::extract_receiver::<#cls, _>(#bound_ref)
495-
}
496-
}
494+
let call = quote_spanned! { *span =>
495+
#pyo3_path::impl_::extract_argument::extract_receiver::<#cls, _>(#bound_ref)
496+
};
497+
quote! { unsafe { #call } }
497498
}
498499
};
499500
error_mode.handle_error(receiver, ctx)

pyo3-macros-backend/src/params.rs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -286,15 +286,18 @@ pub(crate) fn impl_regular_arg_param(
286286
)?
287287
}
288288
} else {
289-
quote_arg_span! {
290-
unsafe {
291-
#pyo3_path::impl_::extract_argument::from_py_with_required(
292-
#arg_value.as_deref(),
293-
#name_str,
294-
#extractor,
295-
)
296-
}?
297-
}
289+
// The `unsafe` token is deliberately spanned at the macro call site (plain
290+
// `quote!`) so that `#![forbid(unsafe_code)]` in user code doesn't reject the
291+
// generated unsafe block.
292+
let call = quote_arg_span! {
293+
#pyo3_path::impl_::extract_argument::from_py_with_required(
294+
#arg_value.as_deref(),
295+
#name_str,
296+
#extractor,
297+
)
298+
};
299+
let unsafe_call = quote! { unsafe { #call } };
300+
quote_arg_span! { #unsafe_call? }
298301
}
299302
} else if let Some(default) = default {
300303
let holder = holders.push_holder(arg.ty.span());
@@ -311,14 +314,16 @@ pub(crate) fn impl_regular_arg_param(
311314
}
312315
} else {
313316
let holder = holders.push_holder(arg.ty.span());
314-
quote_arg_span! {
315-
unsafe {
316-
#pyo3_path::impl_::extract_argument::extract_required_argument(
317-
#arg_value,
318-
&mut #holder,
319-
#name_str
320-
)
321-
}?
322-
}
317+
// As above, the `unsafe` token is spanned at the macro call site to be compatible
318+
// with `#![forbid(unsafe_code)]` in user code.
319+
let call = quote_arg_span! {
320+
#pyo3_path::impl_::extract_argument::extract_required_argument(
321+
#arg_value,
322+
&mut #holder,
323+
#name_str
324+
)
325+
};
326+
let unsafe_call = quote! { unsafe { #call } };
327+
quote_arg_span! { #unsafe_call? }
323328
}
324329
}

pyo3-macros-backend/src/pyclass.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ fn impl_simple_enum(
11401140
type Error = #pyo3_path::PyErr;
11411141
#output_type
11421142

1143-
fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> #pyo3_path::PyResult<Self::Output> {
1143+
fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> #pyo3_path::PyResult<#pyo3_path::Bound<'py, Self>> {
11441144
// TODO(icxolu): switch this to lookup the variants on the type object, once that is immutable
11451145
static SINGLETON: [#pyo3_path::sync::PyOnceLock<#pyo3_path::Py<#cls>>; #num] =
11461146
[const { #pyo3_path::sync::PyOnceLock::<#pyo3_path::Py<#cls>>::new() }; #num];
@@ -1269,7 +1269,7 @@ fn impl_complex_enum(
12691269
type Error = #pyo3_path::PyErr;
12701270
#output_type
12711271

1272-
fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> #pyo3_path::PyResult<Self::Output> {
1272+
fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> #pyo3_path::PyResult<#pyo3_path::Bound<'py, Self>> {
12731273
match self {
12741274
#(#match_arms)*
12751275
}
@@ -2762,7 +2762,7 @@ impl<'a> PyClassImplsBuilder<'a> {
27622762
type Error = #pyo3_path::PyErr;
27632763
#output_type
27642764

2765-
fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> #pyo3_path::PyResult<Self::Output> {
2765+
fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> #pyo3_path::PyResult<#pyo3_path::Bound<'py, Self>> {
27662766
#pyo3_path::Bound::new(py, self)
27672767
}
27682768
}

tests/ui/invalid_cancel_handle.stderr

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ help: the trait `pyo3::PyClass` is implemented for `pyo3::coroutine::Coroutine`
8484
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
8585
= note: required for `CancelHandle` to implement `FromPyObject<'_, '_>`
8686
= note: required for `CancelHandle` to implement `pyo3::impl_::extract_argument::PyFunctionArgument<'_, '_, '_, true>`
87-
note: required by a bound in `pyo3::impl_::extract_argument::extract_argument`
87+
note: required by a bound in `pyo3::impl_::extract_argument::extract_required_argument`
8888
--> src/impl_/extract_argument.rs
8989
|
90-
| pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
91-
| ---------------- required by a bound in this function
90+
| pub unsafe fn extract_required_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
91+
| ------------------------- required by a bound in this function
9292
...
9393
| T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>,
94-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`
94+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_required_argument`
9595
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
9696

9797
error[E0277]: `CancelHandle` cannot be used as a Python function argument
@@ -124,14 +124,14 @@ help: the following other types implement trait `pyo3::impl_::extract_argument::
124124
| |______________________^ `&'holder mut T` implements `pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, '_, false>`
125125
= note: required for `CancelHandle` to implement `FromPyObject<'_, '_>`
126126
= note: required for `CancelHandle` to implement `pyo3::impl_::extract_argument::PyFunctionArgument<'_, '_, '_, true>`
127-
note: required by a bound in `pyo3::impl_::extract_argument::extract_argument`
127+
note: required by a bound in `pyo3::impl_::extract_argument::extract_required_argument`
128128
--> src/impl_/extract_argument.rs
129129
|
130-
| pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
131-
| ---------------- required by a bound in this function
130+
| pub unsafe fn extract_required_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
131+
| ------------------------- required by a bound in this function
132132
...
133133
| T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>,
134-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`
134+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_required_argument`
135135

136136
error[E0277]: `CancelHandle` cannot be used as a Python function argument
137137
--> tests/ui/invalid_cancel_handle.rs:24:50
@@ -148,14 +148,14 @@ help: the trait `pyo3::impl_::pyclass::ExtractPyClassWithClone` is implemented f
148148
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
149149
= note: required for `CancelHandle` to implement `FromPyObject<'_, '_>`
150150
= note: required for `CancelHandle` to implement `pyo3::impl_::extract_argument::PyFunctionArgument<'_, '_, '_, true>`
151-
note: required by a bound in `pyo3::impl_::extract_argument::extract_argument`
151+
note: required by a bound in `pyo3::impl_::extract_argument::extract_required_argument`
152152
--> src/impl_/extract_argument.rs
153153
|
154-
| pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
155-
| ---------------- required by a bound in this function
154+
| pub unsafe fn extract_required_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
155+
| ------------------------- required by a bound in this function
156156
...
157157
| T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>,
158-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`
158+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_required_argument`
159159
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
160160

161161
error: aborting due to 9 previous errors

tests/ui/invalid_pyclass_args.default.stderr

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -413,15 +413,6 @@ error[E0609]: no field `162543` on type `&Coord3`
413413
|
414414
= note: available fields are: `0`, `1`, `2`
415415

416-
warning: use of deprecated associated constant `pyo3::impl_::deprecated::HasAutomaticFromPyObject::<true>::MSG`: The `FromPyObject` implementation for `#[pyclass]` types which implement `Clone` is changing to an opt-in option. Use `#[pyclass(from_py_object)]` to opt-in to the `FromPyObject` derive now, or `#[pyclass(skip_from_py_object)]` to skip the `FromPyObject` implementation.
417-
--> tests/ui/invalid_pyclass_args.rs:243:1
418-
|
419-
243 | #[pyclass]
420-
| ^^^^^^^^^^
421-
|
422-
= note: `#[warn(deprecated)]` on by default
423-
= note: this warning originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
424-
425416
error[E0277]: `Box<dyn std::error::Error + Send + Sync>` cannot be used as a Python function argument
426417
--> tests/ui/invalid_pyclass_args.rs:252:12
427418
|
@@ -442,14 +433,14 @@ error[E0277]: `Box<dyn std::error::Error + Send + Sync>` cannot be used as a Pyt
442433
and $N others
443434
= note: required for `Box<dyn std::error::Error + Send + Sync>` to implement `pyo3::FromPyObject<'_, '_>`
444435
= note: required for `Box<dyn std::error::Error + Send + Sync>` to implement `pyo3::impl_::extract_argument::PyFunctionArgument<'_, '_, '_, true>`
445-
note: required by a bound in `pyo3::impl_::extract_argument::extract_argument`
436+
note: required by a bound in `pyo3::impl_::extract_argument::extract_required_argument`
446437
--> src/impl_/extract_argument.rs
447438
|
448-
| pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
449-
| ---------------- required by a bound in this function
439+
| pub unsafe fn extract_required_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
440+
| ------------------------- required by a bound in this function
450441
...
451442
| T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>,
452-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`
443+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_required_argument`
453444

454445
error[E0277]: `Box<dyn std::error::Error + Send + Sync>` cannot be used as a Python function argument
455446
--> tests/ui/invalid_pyclass_args.rs:252:12
@@ -471,14 +462,14 @@ error[E0277]: `Box<dyn std::error::Error + Send + Sync>` cannot be used as a Pyt
471462
and $N others
472463
= note: required for `Box<dyn std::error::Error + Send + Sync>` to implement `pyo3::FromPyObject<'_, '_>`
473464
= note: required for `Box<dyn std::error::Error + Send + Sync>` to implement `pyo3::impl_::extract_argument::PyFunctionArgument<'_, '_, '_, true>`
474-
note: required by a bound in `pyo3::impl_::extract_argument::extract_argument`
465+
note: required by a bound in `pyo3::impl_::extract_argument::extract_required_argument`
475466
--> src/impl_/extract_argument.rs
476467
|
477-
| pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
478-
| ---------------- required by a bound in this function
468+
| pub unsafe fn extract_required_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
469+
| ------------------------- required by a bound in this function
479470
...
480471
| T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>,
481-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`
472+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_required_argument`
482473

483474
error[E0277]: the trait bound `dyn std::error::Error + Send + Sync: Clone` is not satisfied
484475
--> tests/ui/invalid_pyclass_args.rs:252:12
@@ -509,14 +500,14 @@ help: the following other types implement trait `pyo3::impl_::extract_argument::
509500
= note: required for `Box<dyn std::error::Error + Send + Sync>` to implement `Clone`
510501
= note: required for `Box<dyn std::error::Error + Send + Sync>` to implement `pyo3::FromPyObject<'_, '_>`
511502
= note: required for `Box<dyn std::error::Error + Send + Sync>` to implement `pyo3::impl_::extract_argument::PyFunctionArgument<'_, '_, '_, true>`
512-
note: required by a bound in `pyo3::impl_::extract_argument::extract_argument`
503+
note: required by a bound in `pyo3::impl_::extract_argument::extract_required_argument`
513504
--> src/impl_/extract_argument.rs
514505
|
515-
| pub fn extract_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
516-
| ---------------- required by a bound in this function
506+
| pub unsafe fn extract_required_argument<'a, 'holder, 'py, T, const IMPLEMENTS_FROMPYOBJECT: bool>(
507+
| ------------------------- required by a bound in this function
517508
...
518509
| T: PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>,
519-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument`
510+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_required_argument`
520511

521512
error[E0034]: multiple applicable items in scope
522513
--> tests/ui/invalid_pyclass_args.rs:259:1
@@ -554,7 +545,7 @@ note: candidate #2 is defined in an impl for the type `NewFromFieldsWithManualNe
554545
| ^^^^^^^^^^^^
555546
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)
556547

557-
error: aborting due to 50 previous errors; 1 warning emitted
548+
error: aborting due to 50 previous errors
558549

559550
Some errors have detailed explanations: E0034, E0119, E0277, E0369, E0592, E0609.
560551
For more information about an error, try `rustc --explain E0034`.

0 commit comments

Comments
 (0)