Skip to content

Commit 3d44744

Browse files
committed
Add PyTupleTyped
1 parent 6c20c3e commit 3d44744

3 files changed

Lines changed: 69 additions & 7 deletions

File tree

vm/src/builtins/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub(crate) mod staticmethod;
7070
pub use staticmethod::PyStaticMethod;
7171
pub(crate) mod traceback;
7272
pub use traceback::PyTraceback;
73-
pub(crate) mod tuple;
73+
pub mod tuple;
7474
pub use tuple::PyTuple;
7575
pub(crate) mod weakproxy;
7676
pub use weakproxy::PyWeakProxy;

vm/src/builtins/tuple.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use crossbeam_utils::atomic::AtomicCell;
22
use std::fmt;
3+
use std::marker::PhantomData;
34

45
use super::pytype::PyTypeRef;
56
use crate::common::hash::PyHash;
67
use crate::function::OptionalArg;
78
use crate::pyobject::{
89
self, BorrowValue, Either, IdProtocol, IntoPyObject, PyArithmaticValue, PyClassImpl,
9-
PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
10+
PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TransmuteFromObject,
11+
TryFromObject, TypeProtocol,
1012
};
1113
use crate::sequence::{self, SimpleSeq};
1214
use crate::sliceable::PySliceableSequence;
@@ -262,7 +264,7 @@ impl Iterable for PyTuple {
262264

263265
#[pyclass(module = false, name = "tuple_iterator")]
264266
#[derive(Debug)]
265-
pub struct PyTupleIterator {
267+
pub(crate) struct PyTupleIterator {
266268
position: AtomicCell<usize>,
267269
tuple: PyTupleRef,
268270
}
@@ -287,9 +289,35 @@ impl PyIter for PyTupleIterator {
287289
}
288290
}
289291

290-
pub fn init(context: &PyContext) {
291-
let tuple_type = &context.types.tuple_type;
292-
PyTuple::extend_class(context, tuple_type);
293-
292+
pub(crate) fn init(context: &PyContext) {
293+
PyTuple::extend_class(context, &context.types.tuple_type);
294294
PyTupleIterator::extend_class(context, &context.types.tuple_iterator_type);
295295
}
296+
297+
pub struct PyTupleTyped<T> {
298+
// SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the
299+
// elements must be logically valid when transmuted to T
300+
tuple: PyTupleRef,
301+
_marker: PhantomData<Vec<T>>,
302+
}
303+
304+
impl<T: TransmuteFromObject> TryFromObject for PyTupleTyped<T> {
305+
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
306+
let tuple = PyTupleRef::try_from_object(vm, obj)?;
307+
for elem in tuple.borrow_value() {
308+
T::check(vm, elem)?
309+
}
310+
// SAFETY: the contract of TransmuteFromObject upholds the variant on `tuple`
311+
Ok(Self {
312+
tuple,
313+
_marker: PhantomData,
314+
})
315+
}
316+
}
317+
318+
impl<'a, T: 'a> BorrowValue<'a> for PyTupleTyped<T> {
319+
type Borrowed = &'a [T];
320+
fn borrow_value(&'a self) -> Self::Borrowed {
321+
unsafe { &*(self.tuple.borrow_value() as *const [PyObjectRef] as *const [T]) }
322+
}
323+
}

vm/src/pyobject.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,40 @@ pub trait TryFromObject: Sized {
785785
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self>;
786786
}
787787

788+
/// Marks a type that has the exact same layout as PyObjectRef, e.g. a type that is
789+
/// `repr(transparent)` over PyObjectRef.
790+
///
791+
/// # Safety
792+
/// Can only be implemented for types that are `repr(transparent)` over a PyObjectRef `obj`,
793+
/// and logically valid so long as `check(vm, obj)` returns `Ok(())`
794+
pub unsafe trait TransmuteFromObject: Sized {
795+
fn check(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<()>;
796+
}
797+
798+
unsafe impl<T: PyValue> TransmuteFromObject for PyRef<T> {
799+
fn check(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<()> {
800+
let class = T::class(vm);
801+
if obj.isinstance(class) {
802+
if obj.payload_is::<T>() {
803+
Ok(())
804+
} else {
805+
Err(vm.new_runtime_error(format!(
806+
"Unexpected payload '{}' for type '{}'",
807+
class.name,
808+
obj.class().name,
809+
)))
810+
}
811+
} else {
812+
let expected_type = &class.name;
813+
let actual_type = &obj.class().name;
814+
Err(vm.new_type_error(format!(
815+
"Expected type '{}', not '{}'",
816+
expected_type, actual_type,
817+
)))
818+
}
819+
}
820+
}
821+
788822
pub trait IntoPyRef<T: PyObjectPayload> {
789823
fn into_pyref(self, vm: &VirtualMachine) -> PyRef<T>;
790824
}

0 commit comments

Comments
 (0)