Skip to content

Commit

Permalink
Fix lifetime safety bug of AsPyRef::as_ref(). (#876)
Browse files Browse the repository at this point in the history
* Fix lifetime safety bug of AsPyRef::as_ref().

Fixes #875.

* Add test for AsPyRef's lifetimes.
  • Loading branch information
m-ou-se authored Apr 20, 2020
1 parent a58a1cf commit dcab478
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,15 @@ impl<T> Py<T> {
pub trait AsPyRef: Sized {
type Target;
/// Return reference to object.
fn as_ref(&self, py: Python<'_>) -> &Self::Target;
fn as_ref<'p>(&'p self, py: Python<'p>) -> &'p Self::Target;
}

impl<T> AsPyRef for Py<T>
where
T: PyTypeInfo,
{
type Target = T::AsRefTarget;
fn as_ref(&self, _py: Python) -> &Self::Target {
fn as_ref<'p>(&'p self, _py: Python<'p>) -> &'p Self::Target {
let any = self as *const Py<T> as *const PyAny;
unsafe { PyDowncastImpl::unchecked_downcast(&*any) }
}
Expand Down
2 changes: 1 addition & 1 deletion src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ impl PyObject {

impl AsPyRef for PyObject {
type Target = PyAny;
fn as_ref(&self, _py: Python) -> &PyAny {
fn as_ref<'p>(&'p self, _py: Python<'p>) -> &'p PyAny {
unsafe { &*(self as *const _ as *const PyAny) }
}
}
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 @@ -7,6 +7,7 @@ fn test_compile_errors() {
t.compile_fail("tests/ui/invalid_pymethod_names.rs");
t.compile_fail("tests/ui/missing_clone.rs");
t.compile_fail("tests/ui/reject_generics.rs");
t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs");
// Since the current minimum nightly(2020-01-20) has a different error message,
// we skip this test.
// TODO(kngwyu): Remove this `if` when we update minimum nightly.
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/wrong_aspyref_lifetimes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use pyo3::{types::PyDict, AsPyRef, Py, PyNativeType, Python};

fn main() {
let gil = Python::acquire_gil();
let dict: Py<PyDict> = PyDict::new(gil.python()).into();
let dict: &PyDict = dict.as_ref(gil.python());
drop(gil);

let _py: Python = dict.py(); // Obtain a Python<'p> without GIL.
}
10 changes: 10 additions & 0 deletions tests/ui/wrong_aspyref_lifetimes.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error[E0505]: cannot move out of `gil` because it is borrowed
--> $DIR/wrong_aspyref_lifetimes.rs:7:10
|
6 | let dict: &PyDict = dict.as_ref(gil.python());
| --- borrow of `gil` occurs here
7 | drop(gil);
| ^^^ move out of `gil` occurs here
8 |
9 | let _py: Python = dict.py(); // Obtain a Python<'p> without GIL.
| ---- borrow later used here

0 comments on commit dcab478

Please sign in to comment.