Skip to content

Commit 14ce76e

Browse files
committed
PyTupleTyped as alias of PyTuple
1 parent 0948971 commit 14ce76e

File tree

9 files changed

+134
-109
lines changed

9 files changed

+134
-109
lines changed

vm/src/builtins/function.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use super::{
88
#[cfg(feature = "jit")]
99
use crate::common::lock::OnceCell;
1010
use crate::common::lock::PyMutex;
11-
use crate::convert::{ToPyObject, TryFromObject};
1211
use crate::function::ArgMapping;
1312
use crate::object::{Traverse, TraverseFn};
1413
use crate::{
@@ -32,7 +31,7 @@ pub struct PyFunction {
3231
code: PyRef<PyCode>,
3332
globals: PyDictRef,
3433
builtins: PyObjectRef,
35-
closure: Option<PyTupleTyped<PyCellRef>>,
34+
closure: Option<PyRef<PyTupleTyped<PyCellRef>>>,
3635
defaults_and_kwdefaults: PyMutex<(Option<PyTupleRef>, Option<PyDictRef>)>,
3736
name: PyMutex<PyStrRef>,
3837
qualname: PyMutex<PyStrRef>,
@@ -47,7 +46,9 @@ pub struct PyFunction {
4746
unsafe impl Traverse for PyFunction {
4847
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
4948
self.globals.traverse(tracer_fn);
50-
self.closure.traverse(tracer_fn);
49+
if let Some(closure) = self.closure.as_ref() {
50+
closure.as_untyped().traverse(tracer_fn);
51+
}
5152
self.defaults_and_kwdefaults.traverse(tracer_fn);
5253
}
5354
}
@@ -58,7 +59,7 @@ impl PyFunction {
5859
pub(crate) fn new(
5960
code: PyRef<PyCode>,
6061
globals: PyDictRef,
61-
closure: Option<PyTupleTyped<PyCellRef>>,
62+
closure: Option<PyRef<PyTupleTyped<PyCellRef>>>,
6263
defaults: Option<PyTupleRef>,
6364
kw_only_defaults: Option<PyDictRef>,
6465
qualname: PyStrRef,
@@ -326,6 +327,7 @@ impl Py<PyFunction> {
326327
) -> PyResult {
327328
#[cfg(feature = "jit")]
328329
if let Some(jitted_code) = self.jitted_code.get() {
330+
use crate::convert::ToPyObject;
329331
match jit::get_jit_args(self, &func_args, jitted_code, vm) {
330332
Ok(args) => {
331333
return Ok(args.invoke().to_pyobject(vm));
@@ -427,7 +429,7 @@ impl PyFunction {
427429
#[pymember]
428430
fn __closure__(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
429431
let zelf = Self::_as_pyref(&zelf, vm)?;
430-
Ok(vm.unwrap_or_none(zelf.closure.clone().map(|x| x.to_pyobject(vm))))
432+
Ok(vm.unwrap_or_none(zelf.closure.clone().map(|x| x.into())))
431433
}
432434

433435
#[pymember]
@@ -612,8 +614,7 @@ impl Constructor for PyFunction {
612614
}
613615

614616
// Validate that all items are cells and create typed tuple
615-
let typed_closure =
616-
PyTupleTyped::<PyCellRef>::try_from_object(vm, closure_tuple.into())?;
617+
let typed_closure = closure_tuple.try_into_typed::<PyCell>(vm)?;
617618
Some(typed_closure)
618619
} else if !args.code.freevars.is_empty() {
619620
return Err(vm.new_type_error("arg 5 (closure) must be tuple"));

vm/src/builtins/tuple.rs

Lines changed: 72 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::common::{
33
hash::{PyHash, PyUHash},
44
lock::PyMutex,
55
};
6-
use crate::object::{Traverse, TraverseFn};
6+
use crate::object::{MaybeTraverse, Traverse, TraverseFn};
77
use crate::{
88
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
99
atomic_func,
@@ -449,6 +449,24 @@ impl Representable for PyTuple {
449449
}
450450
}
451451

452+
impl PyRef<PyTuple> {
453+
pub fn try_into_typed<T: PyPayload>(
454+
self,
455+
vm: &VirtualMachine,
456+
) -> PyResult<PyRef<PyTupleTyped<PyRef<T>>>> {
457+
PyRef::<PyTupleTyped<PyRef<T>>>::try_from_untyped(self, vm)
458+
}
459+
/// # Safety
460+
///
461+
/// The caller must ensure that all elements in the tuple are valid instances
462+
/// of type `T` before calling this method. This is typically verified by
463+
/// calling `try_into_typed` first.
464+
unsafe fn into_typed_unchecked<T: PyPayload>(self) -> PyRef<PyTupleTyped<PyRef<T>>> {
465+
let obj: PyObjectRef = self.into();
466+
unsafe { obj.downcast_unchecked::<PyTupleTyped<PyRef<T>>>() }
467+
}
468+
}
469+
452470
#[pyclass(module = false, name = "tuple_iterator", traverse)]
453471
#[derive(Debug)]
454472
pub(crate) struct PyTupleIterator {
@@ -500,53 +518,75 @@ pub(crate) fn init(context: &Context) {
500518
PyTupleIterator::extend_class(context, context.types.tuple_iterator_type);
501519
}
502520

503-
pub struct PyTupleTyped<T: TransmuteFromObject> {
521+
#[repr(transparent)]
522+
pub struct PyTupleTyped<R> {
504523
// SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the
505524
// elements must be logically valid when transmuted to T
506-
tuple: PyTupleRef,
507-
_marker: PhantomData<Vec<T>>,
525+
tuple: PyTuple,
526+
_marker: PhantomData<R>,
508527
}
509528

510-
unsafe impl<T> Traverse for PyTupleTyped<T>
529+
unsafe impl<R> Traverse for PyTupleTyped<R>
511530
where
512-
T: TransmuteFromObject + Traverse,
531+
R: TransmuteFromObject,
513532
{
514533
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
515534
self.tuple.traverse(tracer_fn);
516535
}
517536
}
518537

519-
impl<T: TransmuteFromObject> TryFromObject for PyTupleTyped<T> {
520-
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
521-
let tuple = PyTupleRef::try_from_object(vm, obj)?;
522-
for elem in &*tuple {
523-
T::check(vm, elem)?
524-
}
525-
// SAFETY: the contract of TransmuteFromObject upholds the variant on `tuple`
526-
Ok(Self {
527-
tuple,
528-
_marker: PhantomData,
529-
})
538+
impl<R: TransmuteFromObject + Traverse> MaybeTraverse for PyTupleTyped<R> {
539+
const IS_TRACE: bool = true;
540+
fn try_traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
541+
self.traverse(tracer_fn);
530542
}
531543
}
532544

533-
impl<T: TransmuteFromObject> AsRef<[T]> for PyTupleTyped<T> {
534-
fn as_ref(&self) -> &[T] {
535-
self.as_slice()
545+
impl<T: PyPayload> PyTupleTyped<PyRef<T>> {
546+
pub fn new_ref(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<Self> {
547+
// SAFETY: PyRef<T> has the same layout as PyObjectRef
548+
unsafe {
549+
let elements: Vec<PyObjectRef> =
550+
std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
551+
let tuple = PyTuple::new_ref(elements, ctx);
552+
tuple.into_typed_unchecked::<T>()
553+
}
536554
}
537555
}
538556

539-
impl<T: TransmuteFromObject> PyTupleTyped<T> {
540-
pub fn empty(vm: &VirtualMachine) -> Self {
541-
Self {
542-
tuple: vm.ctx.empty_tuple.clone(),
543-
_marker: PhantomData,
557+
impl<T: PyPayload> PyRef<PyTupleTyped<PyRef<T>>> {
558+
pub fn into_untyped(self) -> PyRef<PyTuple> {
559+
// SAFETY: PyTupleTyped is transparent over PyTuple
560+
unsafe { std::mem::transmute::<PyRef<PyTupleTyped<PyRef<T>>>, PyRef<PyTuple>>(self) }
561+
}
562+
563+
pub fn try_from_untyped(tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<Self> {
564+
// Check that all elements are of the correct type
565+
for elem in tuple.as_slice() {
566+
<PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
544567
}
568+
// SAFETY: We just verified all elements are of type T, and PyTupleTyped has the same layout as PyTuple
569+
Ok(unsafe { std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTupleTyped<PyRef<T>>>>(tuple) })
545570
}
571+
}
546572

573+
impl<T: PyPayload> Py<PyTupleTyped<PyRef<T>>> {
574+
pub fn as_untyped(&self) -> &Py<PyTuple> {
575+
// SAFETY: PyTupleTyped is transparent over PyTuple
576+
unsafe { std::mem::transmute::<&Py<PyTupleTyped<PyRef<T>>>, &Py<PyTuple>>(self) }
577+
}
578+
}
579+
580+
impl<T: PyPayload> AsRef<[PyRef<T>]> for PyTupleTyped<PyRef<T>> {
581+
fn as_ref(&self) -> &[PyRef<T>] {
582+
self.as_slice()
583+
}
584+
}
585+
586+
impl<T: PyPayload> PyTupleTyped<PyRef<T>> {
547587
#[inline]
548-
pub fn as_slice(&self) -> &[T] {
549-
unsafe { &*(self.tuple.as_slice() as *const [PyObjectRef] as *const [T]) }
588+
pub fn as_slice(&self) -> &[PyRef<T>] {
589+
unsafe { &*(self.tuple.as_slice() as *const [PyObjectRef] as *const [PyRef<T>]) }
550590
}
551591

552592
#[inline]
@@ -560,32 +600,16 @@ impl<T: TransmuteFromObject> PyTupleTyped<T> {
560600
}
561601
}
562602

563-
impl<T: TransmuteFromObject> Clone for PyTupleTyped<T> {
564-
fn clone(&self) -> Self {
565-
Self {
566-
tuple: self.tuple.clone(),
567-
_marker: PhantomData,
568-
}
569-
}
570-
}
571-
572-
impl<T: TransmuteFromObject + fmt::Debug> fmt::Debug for PyTupleTyped<T> {
603+
impl<R: fmt::Debug> fmt::Debug for PyTupleTyped<R> {
573604
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574-
self.as_slice().fmt(f)
575-
}
576-
}
577-
578-
impl<T: TransmuteFromObject> From<PyTupleTyped<T>> for PyTupleRef {
579-
#[inline]
580-
fn from(tup: PyTupleTyped<T>) -> Self {
581-
tup.tuple
605+
self.tuple.as_slice().fmt(f)
582606
}
583607
}
584608

585-
impl<T: TransmuteFromObject> ToPyObject for PyTupleTyped<T> {
609+
impl<T: PyPayload> From<PyRef<PyTupleTyped<PyRef<T>>>> for PyTupleRef {
586610
#[inline]
587-
fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
588-
self.tuple.into()
611+
fn from(tup: PyRef<PyTupleTyped<PyRef<T>>>) -> Self {
612+
tup.into_untyped()
589613
}
590614
}
591615

vm/src/builtins/type.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ unsafe impl crate::object::Traverse for PyType {
6262
pub struct HeapTypeExt {
6363
pub name: PyRwLock<PyStrRef>,
6464
pub qualname: PyRwLock<PyStrRef>,
65-
pub slots: Option<PyTupleTyped<PyStrRef>>,
65+
pub slots: Option<PyRef<PyTupleTyped<PyStrRef>>>,
6666
pub sequence_methods: PySequenceMethods,
6767
pub mapping_methods: PyMappingMethods,
6868
}
@@ -1041,24 +1041,24 @@ impl Constructor for PyType {
10411041
// TODO: Flags is currently initialized with HAS_DICT. Should be
10421042
// updated when __slots__ are supported (toggling the flag off if
10431043
// a class has __slots__ defined).
1044-
let heaptype_slots: Option<PyTupleTyped<PyStrRef>> =
1044+
let heaptype_slots: Option<PyRef<PyTupleTyped<PyStrRef>>> =
10451045
if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
1046-
Some(if x.to_owned().class().is(vm.ctx.types.str_type) {
1047-
PyTupleTyped::<PyStrRef>::try_from_object(
1048-
vm,
1049-
vec![x.to_owned()].into_pytuple(vm).into(),
1050-
)?
1046+
let slots = if x.class().is(vm.ctx.types.str_type) {
1047+
let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
1048+
PyTupleTyped::new_ref(vec![x.to_owned()], &vm.ctx)
10511049
} else {
1052-
let iter = x.to_owned().get_iter(vm)?;
1050+
let iter = x.get_iter(vm)?;
10531051
let elements = {
10541052
let mut elements = Vec::new();
10551053
while let PyIterReturn::Return(element) = iter.next(vm)? {
10561054
elements.push(element);
10571055
}
10581056
elements
10591057
};
1060-
PyTupleTyped::<PyStrRef>::try_from_object(vm, elements.into_pytuple(vm).into())?
1061-
})
1058+
let tuple = elements.into_pytuple(vm);
1059+
tuple.try_into_typed(vm)?
1060+
};
1061+
Some(slots)
10621062
} else {
10631063
None
10641064
};
@@ -1082,7 +1082,7 @@ impl Constructor for PyType {
10821082
let heaptype_ext = HeapTypeExt {
10831083
name: PyRwLock::new(name),
10841084
qualname: PyRwLock::new(qualname),
1085-
slots: heaptype_slots.to_owned(),
1085+
slots: heaptype_slots.clone(),
10861086
sequence_methods: PySequenceMethods::default(),
10871087
mapping_methods: PyMappingMethods::default(),
10881088
};

vm/src/convert/try_from.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ where
7878
#[inline]
7979
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
8080
let class = T::class(&vm.ctx);
81-
if obj.fast_isinstance(class) {
81+
let result = if obj.fast_isinstance(class) {
8282
obj.downcast()
83-
.map_err(|obj| vm.new_downcast_runtime_error(class, &obj))
8483
} else {
85-
Err(vm.new_downcast_type_error(class, &obj))
86-
}
84+
Err(obj)
85+
};
86+
result.map_err(|obj| vm.new_downcast_type_error(class, &obj))
8787
}
8888
}
8989

vm/src/frame.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
PySlice, PyStr, PyStrInterned, PyStrRef, PyTraceback, PyType,
88
asyncgenerator::PyAsyncGenWrappedValue,
99
function::{PyCell, PyCellRef, PyFunction},
10-
tuple::{PyTuple, PyTupleRef, PyTupleTyped},
10+
tuple::{PyTuple, PyTupleRef},
1111
},
1212
bytecode,
1313
convert::{IntoObject, ToPyResult},
@@ -1346,11 +1346,14 @@ impl ExecutingFrame<'_> {
13461346
#[cfg_attr(feature = "flame-it", flame("Frame"))]
13471347
fn import(&mut self, vm: &VirtualMachine, module_name: Option<&Py<PyStr>>) -> PyResult<()> {
13481348
let module_name = module_name.unwrap_or(vm.ctx.empty_str);
1349-
let from_list = <Option<PyTupleTyped<PyStrRef>>>::try_from_object(vm, self.pop_value())?
1350-
.unwrap_or_else(|| PyTupleTyped::empty(vm));
1349+
let top = self.pop_value();
1350+
let from_list = match <Option<PyTupleRef>>::try_from_object(vm, top)? {
1351+
Some(from_list) => from_list.try_into_typed::<PyStr>(vm)?,
1352+
None => vm.ctx.empty_tuple_typed().to_owned(),
1353+
};
13511354
let level = usize::try_from_object(vm, self.pop_value())?;
13521355

1353-
let module = vm.import_from(module_name, from_list, level)?;
1356+
let module = vm.import_from(module_name, &from_list, level)?;
13541357

13551358
self.push_value(module);
13561359
Ok(())
@@ -1839,7 +1842,8 @@ impl ExecutingFrame<'_> {
18391842
.expect("Second to top value on the stack must be a code object");
18401843

18411844
let closure = if flags.contains(bytecode::MakeFunctionFlags::CLOSURE) {
1842-
Some(PyTupleTyped::try_from_object(vm, self.pop_value()).unwrap())
1845+
let tuple = PyTupleRef::try_from_object(vm, self.pop_value()).unwrap();
1846+
Some(tuple.try_into_typed(vm).expect("This is a compiler bug"))
18431847
} else {
18441848
None
18451849
};

0 commit comments

Comments
 (0)