diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 428b78e5c9d..fd0628440d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -269,9 +269,10 @@ jobs: "3.13t", "3.14-dev", "3.14t-dev", - "pypy3.9", - "pypy3.10", - "pypy3.11", + # FIXME: using nightly for 5142, should consider what to do before merging + #"pypy3.9", + "pypy3.10-nightly", + "pypy3.11-nightly", "graalpy24.2", ] platform: diff --git a/pyo3-ffi/src/modsupport.rs b/pyo3-ffi/src/modsupport.rs index 56a68fe2613..25db90a5988 100644 --- a/pyo3-ffi/src/modsupport.rs +++ b/pyo3-ffi/src/modsupport.rs @@ -73,6 +73,7 @@ extern "C" { // skipped PyModule_AddStringMacro pub fn PyModule_SetDocString(arg1: *mut PyObject, arg2: *const c_char) -> c_int; pub fn PyModule_AddFunctions(arg1: *mut PyObject, arg2: *mut PyMethodDef) -> c_int; + #[cfg_attr(PyPy, link_name = "PyPyModule_ExecDef")] pub fn PyModule_ExecDef(module: *mut PyObject, def: *mut PyModuleDef) -> c_int; } diff --git a/pyo3-macros-backend/src/module.rs b/pyo3-macros-backend/src/module.rs index 860a3b6d857..c995d714325 100644 --- a/pyo3-macros-backend/src/module.rs +++ b/pyo3-macros-backend/src/module.rs @@ -10,7 +10,7 @@ use crate::{ get_doc, pyclass::PyClassPyO3Option, pyfunction::{impl_wrap_pyfunction, PyFunctionOptions}, - utils::{has_attribute, has_attribute_with_namespace, Ctx, IdentOrStr, LitCStr}, + utils::{has_attribute, has_attribute_with_namespace, Ctx, IdentOrStr, LitCStr, PythonDoc}, }; use proc_macro2::{Span, TokenStream}; use quote::quote; @@ -357,23 +357,15 @@ pub fn pymodule_module_impl( #[cfg(not(feature = "experimental-inspect"))] let introspection_id = quote! {}; - let module_def = quote! {{ - use #pyo3_path::impl_::pymodule as impl_; - const INITIALIZER: impl_::ModuleInitializer = impl_::ModuleInitializer(__pyo3_pymodule); - unsafe { - impl_::ModuleDef::new( - __PYO3_NAME, - #doc, - INITIALIZER - ) - } - }}; + let gil_used = options.gil_used.map_or(true, |op| op.value.value); + let initialization = module_initialization( &name, ctx, - module_def, + quote! { __pyo3_pymodule }, options.submodule.is_some(), - options.gil_used.map_or(true, |op| op.value.value), + gil_used, + doc, ); let module_consts_names = module_consts.iter().map(|i| i.unraw().to_string()); @@ -423,12 +415,15 @@ pub fn pymodule_function_impl( let vis = &function.vis; let doc = get_doc(&function.attrs, None, ctx); + let gil_used = options.gil_used.map_or(true, |op| op.value.value); + let initialization = module_initialization( &name, ctx, - quote! { MakeDef::make_def() }, + quote! { ModuleExec::__pyo3_module_exec }, false, - options.gil_used.map_or(true, |op| op.value.value), + gil_used, + doc, ); #[cfg(feature = "experimental-inspect")] @@ -461,20 +456,9 @@ pub fn pymodule_function_impl( // (and `super` doesn't always refer to the outer scope, e.g. if the `#[pymodule] is // inside a function body) #[allow(unknown_lints, non_local_definitions)] - impl #ident::MakeDef { - const fn make_def() -> #pyo3_path::impl_::pymodule::ModuleDef { - fn __pyo3_pymodule(module: &#pyo3_path::Bound<'_, #pyo3_path::types::PyModule>) -> #pyo3_path::PyResult<()> { - #ident(#(#module_args),*) - } - - const INITIALIZER: #pyo3_path::impl_::pymodule::ModuleInitializer = #pyo3_path::impl_::pymodule::ModuleInitializer(__pyo3_pymodule); - unsafe { - #pyo3_path::impl_::pymodule::ModuleDef::new( - #ident::__PYO3_NAME, - #doc, - INITIALIZER - ) - } + impl #ident::ModuleExec { + fn __pyo3_module_exec(module: &#pyo3_path::Bound<'_, #pyo3_path::types::PyModule>) -> #pyo3_path::PyResult<()> { + #ident(#(#module_args),*) } } }) @@ -483,9 +467,10 @@ pub fn pymodule_function_impl( fn module_initialization( name: &syn::Ident, ctx: &Ctx, - module_def: TokenStream, + module_exec: TokenStream, is_submodule: bool, gil_used: bool, + doc: PythonDoc, ) -> TokenStream { let Ctx { pyo3_path, .. } = ctx; let pyinit_symbol = format!("PyInit_{name}"); @@ -496,9 +481,27 @@ fn module_initialization( #[doc(hidden)] pub const __PYO3_NAME: &'static ::std::ffi::CStr = #pyo3_name; - pub(super) struct MakeDef; + // This structure exists for `fn` modules declared within `fn` bodies, where due to the hidden + // module (used for importing) the `fn` to initialize the module cannot be seen from the #module_def + // declaration just below. #[doc(hidden)] - pub static _PYO3_DEF: #pyo3_path::impl_::pymodule::ModuleDef = #module_def; + pub(super) struct ModuleExec; + + #[doc(hidden)] + pub static _PYO3_DEF: #pyo3_path::impl_::pymodule::ModuleDef = { + use #pyo3_path::impl_::pymodule as impl_; + + unsafe extern "C" fn __pyo3_module_exec(module: *mut #pyo3_path::ffi::PyObject) -> ::std::os::raw::c_int { + #pyo3_path::impl_::trampoline::module_exec(module, #module_exec) + } + + static SLOTS: impl_::PyModuleSlots<4> = impl_::PyModuleSlotsBuilder::new() + .with_mod_exec(__pyo3_module_exec) + .with_gil_used(#gil_used) + .build(); + + impl_::ModuleDef::new(__PYO3_NAME, #doc, &SLOTS) + }; #[doc(hidden)] // so wrapped submodules can see what gil_used is pub static __PYO3_GIL_USED: bool = #gil_used; @@ -510,7 +513,10 @@ fn module_initialization( #[doc(hidden)] #[export_name = #pyinit_symbol] pub unsafe extern "C" fn __pyo3_init() -> *mut #pyo3_path::ffi::PyObject { - unsafe { #pyo3_path::impl_::trampoline::module_init(|py| _PYO3_DEF.make_module(py, #gil_used)) } + _PYO3_DEF.init_multi_phase( + unsafe { #pyo3_path::Python::assume_gil_acquired() }, + #gil_used + ) } }); } diff --git a/pytests/tests/test_misc.py b/pytests/tests/test_misc.py index dd7f8007e81..117af8bf419 100644 --- a/pytests/tests/test_misc.py +++ b/pytests/tests/test_misc.py @@ -24,6 +24,7 @@ def test_multiple_imports_same_interpreter_ok(): spec = importlib.util.find_spec("pyo3_pytests.pyo3_pytests") module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) assert dir(module) == dir(pyo3_pytests.pyo3_pytests) diff --git a/src/impl_/pymodule.rs b/src/impl_/pymodule.rs index 08b1ead7584..9c003d3fa07 100644 --- a/src/impl_/pymodule.rs +++ b/src/impl_/pymodule.rs @@ -1,6 +1,13 @@ //! Implementation details of `#[pymodule]` which need to be accessible from proc-macro generated code. -use std::{cell::UnsafeCell, ffi::CStr, marker::PhantomData}; +#[cfg(Py_3_13)] +use std::sync::Once; +use std::{ + cell::UnsafeCell, + ffi::CStr, + marker::PhantomData, + os::raw::{c_int, c_void}, +}; #[cfg(all( not(any(PyPy, GraalPy)), @@ -15,26 +22,23 @@ use portable_atomic::AtomicI64; not(all(windows, Py_LIMITED_API, not(Py_3_10))), target_has_atomic = "64", ))] -use std::sync::atomic::AtomicI64; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicI64, Ordering}; #[cfg(not(any(PyPy, GraalPy)))] use crate::exceptions::PyImportError; -#[cfg(all(not(Py_LIMITED_API), Py_GIL_DISABLED))] -use crate::PyErr; use crate::{ ffi, impl_::pymethods::PyMethodDef, sync::GILOnceCell, - types::{PyCFunction, PyModule, PyModuleMethods}, - Bound, Py, PyClass, PyResult, PyTypeInfo, Python, + types::{PyAnyMethods, PyCFunction, PyDict, PyModule, PyModuleMethods}, + Bound, Py, PyClass, PyObject, PyResult, PyTypeInfo, Python, }; +use crate::{ffi_ptr_ext::FfiPtrExt, PyErr}; /// `Sync` wrapper of `ffi::PyModuleDef`. pub struct ModuleDef { // wrapped in UnsafeCell so that Rust compiler treats this as interior mutability ffi_def: UnsafeCell, - initializer: ModuleInitializer, /// Interpreter ID where module was initialized (not applicable on PyPy). #[cfg(all( not(any(PyPy, GraalPy)), @@ -45,20 +49,24 @@ pub struct ModuleDef { /// Initialized module object, cached to avoid reinitialization. module: GILOnceCell>, /// Whether or not the module supports running without the GIL - gil_used: AtomicBool, + gil_used: bool, + /// Guard to allow updating the `Py_mod_gil` slot within `ffi_def` + /// before first use. + #[cfg(Py_3_13)] + gil_used_once: Once, } -/// Wrapper to enable initializer to be used in const fns. -pub struct ModuleInitializer(pub for<'py> fn(&Bound<'py, PyModule>) -> PyResult<()>); - unsafe impl Sync for ModuleDef {} impl ModuleDef { /// Make new module definition with given module name. - pub const unsafe fn new( + pub const fn new( name: &'static CStr, doc: &'static CStr, - initializer: ModuleInitializer, + // TODO: it might be nice to make this unsized and not need the + // const N generic parameter, however that might need unsized return values + // or other messy hacks. + slots: &'static PyModuleSlots, ) -> Self { #[allow(clippy::declare_interior_mutable_const)] const INIT: ffi::PyModuleDef = ffi::PyModuleDef { @@ -76,12 +84,14 @@ impl ModuleDef { let ffi_def = UnsafeCell::new(ffi::PyModuleDef { m_name: name.as_ptr(), m_doc: doc.as_ptr(), + // TODO: would be slightly nicer to use `[T]::as_mut_ptr()` here, + // but that requires mut ptr deref on MSRV. + m_slots: slots.0.get() as _, ..INIT }); ModuleDef { ffi_def, - initializer, // -1 is never expected to be a valid interpreter ID #[cfg(all( not(any(PyPy, GraalPy)), @@ -90,16 +100,29 @@ impl ModuleDef { ))] interpreter: AtomicI64::new(-1), module: GILOnceCell::new(), - gil_used: AtomicBool::new(true), + gil_used: true, + #[cfg(Py_3_13)] + gil_used_once: Once::new(), } } - /// Builds a module using user given initializer. Used for [`#[pymodule]`][crate::pymodule]. + + pub fn init_multi_phase(&'static self, _py: Python<'_>, _gil_used: bool) -> *mut ffi::PyObject { + // Once the ffi_def has been used, we can no longer modify, so we set the once. + #[cfg(Py_3_13)] + self.gil_used_once.call_once(|| {}); + unsafe { ffi::PyModuleDef_Init(self.ffi_def.get()) } + } + + /// Builds a module object directly. Used for [`#[pymodule]`][crate::pymodule] submodules. #[cfg_attr(any(Py_LIMITED_API, not(Py_GIL_DISABLED)), allow(unused_variables))] pub fn make_module(&'static self, py: Python<'_>, gil_used: bool) -> PyResult> { // Check the interpreter ID has not changed, since we currently have no way to guarantee // that static data is not reused across interpreters. // // PyPy does not have subinterpreters, so no need to check interpreter ID. + // + // TODO: it should be possible to use the Py_mod_multiple_interpreters slot on sufficiently + // new Python versions to remove the need for this custom logic #[cfg(not(any(PyPy, GraalPy)))] { // PyInterpreterState_Get is only available on 3.9 and later, but is missing @@ -133,34 +156,159 @@ impl ModuleDef { } } } - self.module - .get_or_try_init(py, || { - let module = unsafe { - Py::::from_owned_ptr_or_err( - py, - ffi::PyModule_Create(self.ffi_def.get()), - )? - }; - #[cfg(all(not(Py_LIMITED_API), Py_GIL_DISABLED))] - { - let gil_used_ptr = { - if gil_used { + + // Make a dummy spec, needs a `name` attribute and that seems to be sufficient + // for the loader system + + static SIMPLE_NAMESPACE: GILOnceCell = GILOnceCell::new(); + let simple_ns = SIMPLE_NAMESPACE.import(py, "types", "SimpleNamespace")?; + + let ffi_def = self.ffi_def.get(); + + let name = unsafe { CStr::from_ptr((*ffi_def).m_name).to_str()? }.to_string(); + let kwargs = PyDict::new(py); + kwargs.set_item("name", name)?; + let spec = simple_ns.call((), Some(&kwargs))?; + + // If the first time writing this ffi def, we can inherit `gil_used` from parent + // modules. + // + // TODO: remove this once we default to `gil_used = false` (i.e. assume free-threading + // support in extensions). This is purely a convenience so that users only need to + // annotate the top-level module. + // + // SAFETY: + // - ffi_def is known to be non-null + // - we check slots is non-null + // - it is valid for a slot to be zeroed + // - we are writing to the slot under the protection of a `Once`. + #[cfg(Py_3_13)] + self.gil_used_once.call_once(|| { + // SAFETY: ffi_def is non-null + let slots = unsafe { (*ffi_def).m_slots }; + if !slots.is_null() { + let mut slot_ptr = slots; + // SAFETY: non-null slots pointer + while unsafe { *slot_ptr != ZEROED_SLOT } { + // SAFETY: no other accessors due to call_once guard + let slot = unsafe { &mut *slot_ptr }; + if slot.slot == ffi::Py_mod_gil { + slot.value = if gil_used { ffi::Py_MOD_GIL_USED } else { ffi::Py_MOD_GIL_NOT_USED - } - }; - if unsafe { ffi::PyUnstable_Module_SetGIL(module.as_ptr(), gil_used_ptr) } < 0 { - return Err(PyErr::fetch(py)); + }; + break; } + // SAFETY: we have guaranteed there is a trailer zeroed slot + slot_ptr = unsafe { slot_ptr.add(1) }; + } + } + }); + + self.module + .get_or_try_init(py, || { + let def = self.ffi_def.get(); + let module = unsafe { + ffi::PyModule_FromDefAndSpec(def, spec.as_ptr()).assume_owned_or_err(py)? } - self.initializer.0(module.bind(py))?; - Ok(module) + .downcast_into()?; + if unsafe { ffi::PyModule_ExecDef(module.as_ptr(), def) } != 0 { + return Err(PyErr::fetch(py)); + } + Ok(module.unbind()) }) .map(|py_module| py_module.clone_ref(py)) } } +/// Type of the exec slot used to initialise module contents +pub type ModuleExecSlot = unsafe extern "C" fn(*mut ffi::PyObject) -> c_int; + +/// Builder to create `PyModuleSlots`. The size of the number of slots desired must +/// be known up front, and N needs to be at least one greater than the number of +/// actual slots pushed due to the need to have a zeroed element on the end. +pub struct PyModuleSlotsBuilder { + // values (initially all zeroed) + values: [ffi::PyModuleDef_Slot; N], + // current length + len: usize, +} + +impl PyModuleSlotsBuilder { + #[allow(clippy::new_without_default)] + pub const fn new() -> Self { + Self { + // Because the array is c-style, the empty elements should be zeroed. + // `std::mem::zeroed()` requires msrv 1.75 for const + values: [ZEROED_SLOT; N], + len: 0, + } + } + + pub const fn with_mod_exec(self, exec: ModuleExecSlot) -> Self { + self.push(ffi::Py_mod_exec, exec as *mut c_void) + } + + pub const fn with_gil_used(self, gil_used: bool) -> Self { + #[cfg(Py_3_13)] + { + self.push( + ffi::Py_mod_gil, + if gil_used { + ffi::Py_MOD_GIL_USED + } else { + ffi::Py_MOD_GIL_NOT_USED + }, + ) + } + + #[cfg(not(Py_3_13))] + { + // Silence unused variable warning + let _ = gil_used; + + // Py_mod_gil didn't exist before 3.13, can just make + // this function a noop. + // + // By handling it here we can avoid conditional + // compilation within the macros; they can always emit + // a `.with_gil_used()` call. + self + } + } + + pub const fn build(self) -> PyModuleSlots { + // Required to guarantee there's still a zeroed element + // at the end + assert!( + self.len < N, + "N must be greater than the number of slots pushed" + ); + PyModuleSlots(UnsafeCell::new(self.values)) + } + + const fn push(mut self, slot: c_int, value: *mut c_void) -> Self { + self.values[self.len] = ffi::PyModuleDef_Slot { slot, value }; + self.len += 1; + self + } +} + +/// Wrapper to safely store module slots, to be used in a `ModuleDef`. +pub struct PyModuleSlots(UnsafeCell<[ffi::PyModuleDef_Slot; N]>); + +// It might be possible to avoid this with SyncUnsafeCell in the future +// +// SAFETY: the inner values are only accessed within a `ModuleDef`, +// which only uses them to build the `ffi::ModuleDef`. +unsafe impl Sync for PyModuleSlots {} + +const ZEROED_SLOT: ffi::PyModuleDef_Slot = ffi::PyModuleDef_Slot { + slot: 0, + value: std::ptr::null_mut(), +}; + /// Trait to add an element (class, function...) to a module. /// /// Currently only implemented for classes. @@ -211,7 +359,7 @@ impl PyAddToModule for PyMethodDef { impl PyAddToModule for ModuleDef { fn add_to_module(&'static self, module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_submodule( - self.make_module(module.py(), self.gil_used.load(Ordering::Relaxed))? + self.make_module(module.py(), self.gil_used)? .bind(module.py()), ) } @@ -219,32 +367,37 @@ impl PyAddToModule for ModuleDef { #[cfg(test)] mod tests { - use std::{ - borrow::Cow, - ffi::CStr, - sync::atomic::{AtomicBool, Ordering}, - }; + use std::{borrow::Cow, ffi::CStr, os::raw::c_int}; use crate::{ ffi, - types::{any::PyAnyMethods, module::PyModuleMethods, PyModule}, - Bound, PyResult, Python, + impl_::{ + pymodule::{PyModuleSlots, PyModuleSlotsBuilder}, + trampoline, + }, + types::{any::PyAnyMethods, module::PyModuleMethods}, + Python, }; - use super::{ModuleDef, ModuleInitializer}; + use super::ModuleDef; #[test] fn module_init() { - static MODULE_DEF: ModuleDef = unsafe { - ModuleDef::new( - ffi::c_str!("test_module"), - ffi::c_str!("some doc"), - ModuleInitializer(|m| { + unsafe extern "C" fn module_exec(module: *mut ffi::PyObject) -> c_int { + unsafe { + trampoline::module_exec(module, |m| { m.add("SOME_CONSTANT", 42)?; Ok(()) - }), - ) - }; + }) + } + } + + static SLOTS: PyModuleSlots<2> = PyModuleSlotsBuilder::new() + .with_mod_exec(module_exec) + .build(); + static MODULE_DEF: ModuleDef = + ModuleDef::new(ffi::c_str!("test_module"), ffi::c_str!("some doc"), &SLOTS); + Python::with_gil(|py| { let module = MODULE_DEF.make_module(py, false).unwrap().into_bound(py); assert_eq!( @@ -281,23 +434,36 @@ mod tests { static NAME: &CStr = ffi::c_str!("test_module"); static DOC: &CStr = ffi::c_str!("some doc"); - static INIT_CALLED: AtomicBool = AtomicBool::new(false); - - #[allow(clippy::unnecessary_wraps)] - fn init(_: &Bound<'_, PyModule>) -> PyResult<()> { - INIT_CALLED.store(true, Ordering::SeqCst); - Ok(()) - } + static SLOTS: PyModuleSlots<2> = PyModuleSlotsBuilder::new().build(); unsafe { - let module_def: ModuleDef = ModuleDef::new(NAME, DOC, ModuleInitializer(init)); + let module_def: ModuleDef = ModuleDef::new(NAME, DOC, &SLOTS); assert_eq!((*module_def.ffi_def.get()).m_name, NAME.as_ptr() as _); assert_eq!((*module_def.ffi_def.get()).m_doc, DOC.as_ptr() as _); + assert_eq!((*module_def.ffi_def.get()).m_slots, SLOTS.0.get().cast()); + } + } - Python::with_gil(|py| { - module_def.initializer.0(&py.import("builtins").unwrap()).unwrap(); - assert!(INIT_CALLED.load(Ordering::SeqCst)); - }) + #[test] + #[should_panic] + fn test_module_slots_builder_overflow() { + unsafe extern "C" fn module_exec(_module: *mut ffi::PyObject) -> c_int { + 0 + } + + PyModuleSlotsBuilder::<0>::new().with_mod_exec(module_exec); + } + + #[test] + #[should_panic] + fn test_module_slots_builder_overflow_2() { + unsafe extern "C" fn module_exec(_module: *mut ffi::PyObject) -> c_int { + 0 } + + PyModuleSlotsBuilder::<2>::new() + .with_mod_exec(module_exec) + .with_mod_exec(module_exec) + .build(); } } diff --git a/src/impl_/trampoline.rs b/src/impl_/trampoline.rs index 01f8f5e8b0e..6207b8452a7 100644 --- a/src/impl_/trampoline.rs +++ b/src/impl_/trampoline.rs @@ -9,17 +9,24 @@ use std::{ panic::{self, UnwindSafe}, }; -use crate::gil::GILGuard; use crate::{ ffi, ffi_ptr_ext::FfiPtrExt, impl_::callback::PyCallbackOutput, impl_::panic::PanicTrap, - impl_::pymethods::IPowModulo, panic::PanicException, types::PyModule, Py, PyResult, Python, + impl_::pymethods::IPowModulo, panic::PanicException, types::PyModule, PyResult, Python, }; +use crate::{gil::GILGuard, Bound}; #[inline] -pub unsafe fn module_init( - f: for<'py> unsafe fn(Python<'py>) -> PyResult>, -) -> *mut ffi::PyObject { - unsafe { trampoline(|py| f(py).map(|module| module.into_ptr())) } +pub unsafe fn module_exec( + module: *mut ffi::PyObject, + f: for<'a, 'py> fn(&'a Bound<'py, PyModule>) -> PyResult<()>, +) -> c_int { + unsafe { + trampoline(|py| { + let module = module.assume_borrowed_or_err(py)?.downcast::()?; + f(&module)?; + Ok(0) + }) + } } #[inline] diff --git a/tests/ui/invalid_proto_pymethods.stderr b/tests/ui/invalid_proto_pymethods.stderr index 82c99c2ddc3..1f564d18ea0 100644 --- a/tests/ui/invalid_proto_pymethods.stderr +++ b/tests/ui/invalid_proto_pymethods.stderr @@ -22,6 +22,17 @@ error: `text_signature` cannot be used with magic method `__bool__` 46 | #[pyo3(name = "__bool__", text_signature = "")] | ^^^^^^^^^^^^^^ +error[E0592]: duplicate definitions with name `__pymethod___richcmp____` + --> tests/ui/invalid_proto_pymethods.rs:55:1 + | +55 | #[pymethods] + | ^^^^^^^^^^^^ + | | + | duplicate definitions for `__pymethod___richcmp____` + | other definition for `__pymethod___richcmp____` + | + = note: this error originates in the macro `::pyo3::impl_::pyclass::generate_pyclass_richcompare_slot` which comes from the expansion of the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0034]: multiple applicable items in scope --> tests/ui/invalid_proto_pymethods.rs:55:1 | @@ -40,17 +51,6 @@ note: candidate #2 is defined in an impl for the type `EqAndRichcmp` | ^^^^^^^^^^^^ = note: this error originates in the macro `::pyo3::impl_::pyclass::generate_pyclass_richcompare_slot` which comes from the expansion of the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0592]: duplicate definitions with name `__pymethod___richcmp____` - --> tests/ui/invalid_proto_pymethods.rs:55:1 - | -55 | #[pymethods] - | ^^^^^^^^^^^^ - | | - | duplicate definitions for `__pymethod___richcmp____` - | other definition for `__pymethod___richcmp____` - | - = note: this error originates in the macro `::pyo3::impl_::pyclass::generate_pyclass_richcompare_slot` which comes from the expansion of the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0034]: multiple applicable items in scope --> tests/ui/invalid_proto_pymethods.rs:55:1 | diff --git a/tests/ui/pyclass_send.stderr b/tests/ui/pyclass_send.stderr index 1623a5b2183..334c36daa13 100644 --- a/tests/ui/pyclass_send.stderr +++ b/tests/ui/pyclass_send.stderr @@ -1,24 +1,3 @@ -error[E0277]: `*mut c_void` cannot be shared between threads safely - --> tests/ui/pyclass_send.rs:5:8 - | -5 | struct NotSyncNotSend(*mut c_void); - | ^^^^^^^^^^^^^^ `*mut c_void` cannot be shared between threads safely - | - = help: within `NotSyncNotSend`, the trait `Sync` is not implemented for `*mut c_void` -note: required because it appears within the type `NotSyncNotSend` - --> tests/ui/pyclass_send.rs:5:8 - | -5 | struct NotSyncNotSend(*mut c_void); - | ^^^^^^^^^^^^^^ -note: required by a bound in `assert_pyclass_sync` - --> src/impl_/pyclass/assertions.rs - | - | pub const fn assert_pyclass_sync() - | ------------------- required by a bound in this function - | where - | T: PyClassSync + Sync, - | ^^^^ required by this bound in `assert_pyclass_sync` - error[E0277]: `*mut c_void` cannot be sent between threads safely --> tests/ui/pyclass_send.rs:4:1 | @@ -40,27 +19,6 @@ note: required by a bound in `PyClassImpl::ThreadChecker` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `PyClassImpl::ThreadChecker` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: `*mut c_void` cannot be shared between threads safely - --> tests/ui/pyclass_send.rs:8:8 - | -8 | struct SendNotSync(*mut c_void); - | ^^^^^^^^^^^ `*mut c_void` cannot be shared between threads safely - | - = help: within `SendNotSync`, the trait `Sync` is not implemented for `*mut c_void` -note: required because it appears within the type `SendNotSync` - --> tests/ui/pyclass_send.rs:8:8 - | -8 | struct SendNotSync(*mut c_void); - | ^^^^^^^^^^^ -note: required by a bound in `assert_pyclass_sync` - --> src/impl_/pyclass/assertions.rs - | - | pub const fn assert_pyclass_sync() - | ------------------- required by a bound in this function - | where - | T: PyClassSync + Sync, - | ^^^^ required by this bound in `assert_pyclass_sync` - error[E0277]: `*mut c_void` cannot be sent between threads safely --> tests/ui/pyclass_send.rs:11:1 | @@ -119,3 +77,45 @@ note: required by a bound in `SendablePyClass` | pub struct SendablePyClass(PhantomData); | ^^^^ required by this bound in `SendablePyClass` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `*mut c_void` cannot be shared between threads safely + --> tests/ui/pyclass_send.rs:5:8 + | +5 | struct NotSyncNotSend(*mut c_void); + | ^^^^^^^^^^^^^^ `*mut c_void` cannot be shared between threads safely + | + = help: within `NotSyncNotSend`, the trait `Sync` is not implemented for `*mut c_void` +note: required because it appears within the type `NotSyncNotSend` + --> tests/ui/pyclass_send.rs:5:8 + | +5 | struct NotSyncNotSend(*mut c_void); + | ^^^^^^^^^^^^^^ +note: required by a bound in `assert_pyclass_sync` + --> src/impl_/pyclass/assertions.rs + | + | pub const fn assert_pyclass_sync() + | ------------------- required by a bound in this function + | where + | T: PyClassSync + Sync, + | ^^^^ required by this bound in `assert_pyclass_sync` + +error[E0277]: `*mut c_void` cannot be shared between threads safely + --> tests/ui/pyclass_send.rs:8:8 + | +8 | struct SendNotSync(*mut c_void); + | ^^^^^^^^^^^ `*mut c_void` cannot be shared between threads safely + | + = help: within `SendNotSync`, the trait `Sync` is not implemented for `*mut c_void` +note: required because it appears within the type `SendNotSync` + --> tests/ui/pyclass_send.rs:8:8 + | +8 | struct SendNotSync(*mut c_void); + | ^^^^^^^^^^^ +note: required by a bound in `assert_pyclass_sync` + --> src/impl_/pyclass/assertions.rs + | + | pub const fn assert_pyclass_sync() + | ------------------- required by a bound in this function + | where + | T: PyClassSync + Sync, + | ^^^^ required by this bound in `assert_pyclass_sync`