From d7056205023b9fdc87472d03a6b649a0bdbc8b1f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 6 Mar 2016 01:08:40 +0100 Subject: [PATCH] Add support for Rust stable. All functions that depend on PyObject having the same memory layout as *mut ffi::PyObject should now be hidden behind #[cfg(feature="nightly")]. --- .travis.yml | 1 + Makefile | 3 +++ extensions/hello.rs | 2 +- src/err.rs | 4 ++-- src/function.rs | 17 ++++++++++++----- src/lib.rs | 7 +++++-- src/objects/object.rs | 23 ++++++++++++++++------- src/objects/tuple.rs | 3 +++ src/rustobject/method.rs | 34 ++++++++++++++++++++++++++-------- src/rustobject/mod.rs | 9 ++++++++- 10 files changed, 77 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5fd7a8d0..582a2f01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - "3.4" - "3.5" env: + - RUST_VERSION=1.7 - RUST_VERSION=nightly sudo: false install: diff --git a/Makefile b/Makefile index d6bf5855..748b799e 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,10 @@ build: test: build cargo test $(CARGO_FLAGS) +ifeq ($(NIGHTLY),1) +# ast-json output is only supported on nightly python$(PY) tests/check_symbols.py +endif doc: build cargo doc --no-deps $(CARGO_FLAGS) diff --git a/extensions/hello.rs b/extensions/hello.rs index 847802d4..f9d64ba0 100644 --- a/extensions/hello.rs +++ b/extensions/hello.rs @@ -13,7 +13,7 @@ py_module_initializer!(hello, inithello, PyInit_hello, |py, m| { fn run(py: Python, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult { println!("Rust says: Hello Python!"); - for arg in args.as_slice() { + for arg in args.iter(py) { println!("Rust got {}", arg); } if let Some(kwargs) = kwargs { diff --git a/src/err.rs b/src/err.rs index e684cf05..123ffd1e 100644 --- a/src/err.rs +++ b/src/err.rs @@ -199,7 +199,7 @@ impl PyErr { Err(_) => match self.ptype.cast_as::(py) { Ok(_) => py.get_type::(), - Err(_) => py.None().get_type().clone_ref(py) + Err(_) => py.None().get_type(py) } } } @@ -209,7 +209,7 @@ impl PyErr { pub fn get_type(&self, py: Python) -> PyType { match self.ptype.cast_as::(py) { Ok(t) => t.clone_ref(py), - Err(_) => py.None().get_type().clone_ref(py) + Err(_) => py.None().get_type(py) } } diff --git a/src/function.rs b/src/function.rs index 445e32db..f1b9483f 100644 --- a/src/function.rs +++ b/src/function.rs @@ -59,20 +59,27 @@ macro_rules! py_fn_wrap { let _guard = $crate::_detail::PanicGuard::with_message( concat!("Rust panic in py_fn!(", stringify!($f), ")")); let $py: $crate::Python = $crate::_detail::bounded_assume_gil_acquired(&args); - let $args: &$crate::PyTuple = $crate::PyObject::borrow_from_ptr(&args).unchecked_cast_as(); - let $kwargs: Option<&$crate::PyDict> = $crate::_detail::get_kwargs(&kwargs); - $crate::_detail::result_to_ptr($py, $body) + let args: $crate::PyTuple = $crate::PyObject::from_borrowed_ptr($py, args).unchecked_cast_into(); + let kwargs: Option<$crate::PyDict> = $crate::_detail::get_kwargs($py, kwargs); + let ret = { + let $args = &args; + let $kwargs = kwargs.as_ref(); + $crate::_detail::result_to_ptr($py, $body) + }; + $crate::PyDrop::release_ref(args, $py); + $crate::PyDrop::release_ref(kwargs, $py); + ret } wrap::<()> }}; } #[inline] -pub unsafe fn get_kwargs(ptr: &*mut ffi::PyObject) -> Option<&PyDict> { +pub unsafe fn get_kwargs(py: Python, ptr: *mut ffi::PyObject) -> Option { if ptr.is_null() { None } else { - Some(PyObject::borrow_from_ptr(ptr).unchecked_cast_as()) + Some(PyObject::from_borrowed_ptr(py, ptr).unchecked_cast_into()) } } diff --git a/src/lib.rs b/src/lib.rs index 03f87555..ea7dd7e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,9 +16,12 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#![feature(unsafe_no_drop_flag)] // crucial so that PyObject<'p> is binary compatible with *mut ffi::PyObject -#![feature(filling_drop)] // necessary to avoid segfault with unsafe_no_drop_flag (#5016) #![cfg_attr(feature="nightly", feature( + unsafe_no_drop_flag, filling_drop, // (#5016) + // ^ These two are crucial so that PyObject<'p> is binary compatible with + // `*mut ffi::PyObject`, which we use for efficient slice access and in + // some other cases. + const_fn, // for GILProtected::new (#24111) shared, // for std::ptr::Shared (#27730) ))] diff --git a/src/objects/object.rs b/src/objects/object.rs index 6066adb5..59cc0866 100644 --- a/src/objects/object.rs +++ b/src/objects/object.rs @@ -42,8 +42,8 @@ use err::PyResult; /// on `PyObject` to convert to more specific object types. /// /// Most of the interesting methods are provided by the [ObjectProtocol trait](trait.ObjectProtocol.html). -#[unsafe_no_drop_flag] -#[repr(C)] +#[cfg_attr(feature="nightly", unsafe_no_drop_flag)] +#[cfg_attr(feature="nightly", repr(C))] pub struct PyObject { // PyObject owns one reference to the *PyObject // ptr is not null @@ -60,6 +60,7 @@ unsafe impl Sync for PyObject {} /// Dropping a `PyObject` decrements the reference count on the object by 1. impl Drop for PyObject { #[inline] + #[cfg(feature="nightly")] fn drop(&mut self) { // TODO: remove `if` when #[unsafe_no_drop_flag] disappears if unpack_shared(self.ptr) as usize != mem::POST_DROP_USIZE { @@ -67,6 +68,14 @@ impl Drop for PyObject { unsafe { ffi::Py_DECREF(unpack_shared(self.ptr)); } } } + + #[inline] + #[cfg(not(feature="nightly"))] + fn drop(&mut self) { + let _gil_guard = Python::acquire_gil(); + unsafe { ffi::Py_DECREF(unpack_shared(self.ptr)); } + } + } #[inline] @@ -198,6 +207,7 @@ impl PyObject { /// Transmutes an FFI pointer to `&PyObject`. /// Undefined behavior if the pointer is NULL or invalid. #[inline] + #[cfg(feature="nightly")] // needs unsafe_no_drop_flag pub unsafe fn borrow_from_ptr<'a>(ptr : &'a *mut ffi::PyObject) -> &'a PyObject { debug_assert!(!ptr.is_null()); mem::transmute(ptr) @@ -206,6 +216,7 @@ impl PyObject { /// Transmutes a slice of owned FFI pointers to `&[PyObject]`. /// Undefined behavior if any pointer in the slice is NULL or invalid. #[inline] + #[cfg(feature="nightly")] // needs unsafe_no_drop_flag pub unsafe fn borrow_from_owned_ptr_slice<'a>(ptr : &'a [*mut ffi::PyObject]) -> &'a [PyObject] { mem::transmute(ptr) } @@ -217,12 +228,9 @@ impl PyObject { } /// Gets the Python type object for this object's type. - #[inline] - pub fn get_type(&self) -> &PyType { + pub fn get_type(&self, py: Python) -> PyType { unsafe { - let t : &*mut ffi::PyTypeObject = &(*self.as_ptr()).ob_type; - let t : &*mut ffi::PyObject = mem::transmute(t); - PyObject::borrow_from_ptr(t).unchecked_cast_as() + PyType::from_type_ptr(py, (*self.as_ptr()).ob_type) } } @@ -291,6 +299,7 @@ impl PartialEq for PyObject { impl Eq for PyObject { } #[test] +#[cfg(feature="nightly")] // needs unsafe_no_drop_flag fn test_sizeof() { // should be a static_assert, but size_of is not a compile-time const // these are necessary for the transmutes in this module diff --git a/src/objects/tuple.rs b/src/objects/tuple.rs index 598b286c..efbcd3d8 100644 --- a/src/objects/tuple.rs +++ b/src/objects/tuple.rs @@ -72,6 +72,7 @@ impl PyTuple { } #[inline] + #[cfg(feature="nightly")] // needs unsafe_no_drop_flag pub fn as_slice<'a>(&'a self) -> &'a [PyObject] { // This is safe because PyObject has the same memory layout as *mut ffi::PyObject, // and because tuples are immutable. @@ -97,6 +98,7 @@ impl PyTuple { } } +#[cfg(feature="nightly")] // needs unsafe_no_drop_flag impl ::std::ops::Index for PyTuple { type Output = PyObject; @@ -106,6 +108,7 @@ impl ::std::ops::Index for PyTuple { } } +#[cfg(feature="nightly")] // needs unsafe_no_drop_flag impl <'a> IntoIterator for &'a PyTuple { type Item = &'a PyObject; type IntoIter = slice::Iter<'a, PyObject>; diff --git a/src/rustobject/method.rs b/src/rustobject/method.rs index 8e9a8afc..ee273e80 100644 --- a/src/rustobject/method.rs +++ b/src/rustobject/method.rs @@ -38,10 +38,19 @@ macro_rules! py_method_wrap { let _guard = $crate::_detail::PanicGuard::with_message( concat!("Rust panic in py_method!(", stringify!($f), ")")); let $py: $crate::Python = $crate::_detail::bounded_assume_gil_acquired(&args); - let $slf: &$crate::PyObject = $crate::PyObject::borrow_from_ptr(&slf); - let $args: &$crate::PyTuple = $crate::PyObject::borrow_from_ptr(&args).unchecked_cast_as(); - let $kwargs: Option<&$crate::PyDict> = $crate::_detail::get_kwargs(&kwargs); - $crate::_detail::result_to_ptr($py, $body) + let slf: $crate::PyObject = $crate::PyObject::from_borrowed_ptr($py, slf); + let args: $crate::PyTuple = $crate::PyObject::from_borrowed_ptr($py, args).unchecked_cast_into(); + let kwargs: Option<$crate::PyDict> = $crate::_detail::get_kwargs($py, kwargs); + let ret = { + let $slf = &slf; + let $args = &args; + let $kwargs = kwargs.as_ref(); + $crate::_detail::result_to_ptr($py, $body) + }; + $crate::PyDrop::release_ref(slf, $py); + $crate::PyDrop::release_ref(args, $py); + $crate::PyDrop::release_ref(kwargs, $py); + ret } wrap::<()> }}; @@ -211,10 +220,19 @@ macro_rules! py_class_method_wrap { let _guard = $crate::_detail::PanicGuard::with_message( concat!("Rust panic in py_class_method!(", stringify!($f), ")")); let $py: $crate::Python = $crate::_detail::bounded_assume_gil_acquired(&args); - let $slf: &$crate::PyType = $crate::PyObject::borrow_from_ptr(&slf).unchecked_cast_as(); - let $args: &$crate::PyTuple = $crate::PyObject::borrow_from_ptr(&args).unchecked_cast_as(); - let $kwargs: Option<&$crate::PyDict> = $crate::_detail::get_kwargs(&kwargs); - $crate::_detail::result_to_ptr($py, $body) + let slf: $crate::PyType = $crate::PyObject::from_borrowed_ptr($py, slf).unchecked_cast_into(); + let args: $crate::PyTuple = $crate::PyObject::from_borrowed_ptr($py, args).unchecked_cast_into(); + let kwargs: Option<$crate::PyDict> = $crate::_detail::get_kwargs($py, kwargs); + let ret = { + let $slf = &slf; + let $args = &args; + let $kwargs = kwargs.as_ref(); + $crate::_detail::result_to_ptr($py, $body) + }; + $crate::PyDrop::release_ref(slf, $py); + $crate::PyDrop::release_ref(args, $py); + $crate::PyDrop::release_ref(kwargs, $py); + ret } wrap::<()> }}; diff --git a/src/rustobject/mod.rs b/src/rustobject/mod.rs index 017bc949..3582d9e9 100644 --- a/src/rustobject/mod.rs +++ b/src/rustobject/mod.rs @@ -140,8 +140,15 @@ impl PythonBaseObject for PyRustObject where T: 'static + Send, B: } unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) { + // drop_in_place() isn't available in 1.7 stable; + // it'll be stabilized in 1.8 + #[cfg(not(feature="nightly"))] + use std::ptr::read as drop_ptr_target; + #[cfg(feature="nightly")] + use std::ptr::drop_in_place as drop_ptr_target; + let offset = PyRustObject::::offset() as isize; - ptr::read_and_drop((obj as *mut u8).offset(offset) as *mut T); + drop_ptr_target((obj as *mut u8).offset(offset) as *mut T); B::dealloc(py, obj) } }