Skip to content

Commit fff5446

Browse files
committed
PyTupleTyped as alias of PyTuple
1 parent 26d54f7 commit fff5446

File tree

9 files changed

+130
-109
lines changed

9 files changed

+130
-109
lines changed

vm/src/builtins/function.rs

Lines changed: 7 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,
@@ -427,7 +428,7 @@ impl PyFunction {
427428
#[pymember]
428429
fn __closure__(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
429430
let zelf = Self::_as_pyref(&zelf, vm)?;
430-
Ok(vm.unwrap_or_none(zelf.closure.clone().map(|x| x.to_pyobject(vm))))
431+
Ok(vm.unwrap_or_none(zelf.closure.clone().map(|x| x.into_pyobject())))
431432
}
432433

433434
#[pymember]
@@ -612,8 +613,7 @@ impl Constructor for PyFunction {
612613
}
613614

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

vm/src/builtins/tuple.rs

Lines changed: 69 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,19 @@ 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+
unsafe fn into_typed_unchecked<T: PyPayload>(self) -> PyRef<PyTupleTyped<PyRef<T>>> {
460+
let obj: PyObjectRef = self.into();
461+
unsafe { obj.downcast_unchecked::<PyTupleTyped<PyRef<T>>>() }
462+
}
463+
}
464+
452465
#[pyclass(module = false, name = "tuple_iterator", traverse)]
453466
#[derive(Debug)]
454467
pub(crate) struct PyTupleIterator {
@@ -500,53 +513,77 @@ pub(crate) fn init(context: &Context) {
500513
PyTupleIterator::extend_class(context, context.types.tuple_iterator_type);
501514
}
502515

503-
pub struct PyTupleTyped<T: TransmuteFromObject> {
516+
pub struct PyTupleTyped<R> {
504517
// SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the
505518
// elements must be logically valid when transmuted to T
506-
tuple: PyTupleRef,
507-
_marker: PhantomData<Vec<T>>,
519+
tuple: PyTuple,
520+
_marker: PhantomData<R>,
508521
}
509522

510-
unsafe impl<T> Traverse for PyTupleTyped<T>
523+
unsafe impl<R> Traverse for PyTupleTyped<R>
511524
where
512-
T: TransmuteFromObject + Traverse,
525+
R: TransmuteFromObject,
513526
{
514527
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
515528
self.tuple.traverse(tracer_fn);
516529
}
517530
}
518531

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-
})
532+
impl<R: TransmuteFromObject + Traverse> MaybeTraverse for PyTupleTyped<R> {
533+
const IS_TRACE: bool = true;
534+
fn try_traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
535+
self.traverse(tracer_fn);
530536
}
531537
}
532538

533-
impl<T: TransmuteFromObject> AsRef<[T]> for PyTupleTyped<T> {
534-
fn as_ref(&self) -> &[T] {
535-
self.as_slice()
539+
impl<T: PyPayload> PyTupleTyped<PyRef<T>> {
540+
pub fn new_ref(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<Self> {
541+
unsafe {
542+
let elements: Vec<PyObjectRef> =
543+
std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
544+
let tuple = PyTuple::new_ref(elements, ctx);
545+
tuple.into_typed_unchecked::<T>()
546+
}
536547
}
537548
}
538549

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,
550+
impl<T: PyPayload> PyRef<PyTupleTyped<PyRef<T>>> {
551+
pub fn into_pyobject(self) -> PyObjectRef {
552+
self.into_untyped().into()
553+
}
554+
555+
pub fn into_untyped(self) -> PyRef<PyTuple> {
556+
// SAFETY: PyTupleTyped is transparent over PyTuple
557+
unsafe { std::mem::transmute::<PyRef<PyTupleTyped<PyRef<T>>>, PyRef<PyTuple>>(self) }
558+
}
559+
560+
pub fn try_from_untyped(tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<Self> {
561+
// Check that all elements are of the correct type
562+
for elem in tuple.as_slice() {
563+
<PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
544564
}
565+
// SAFETY: We just verified all elements are of type T, and PyTupleTyped has the same layout as PyTuple
566+
Ok(unsafe { std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTupleTyped<PyRef<T>>>>(tuple) })
545567
}
568+
}
546569

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

552589
#[inline]
@@ -560,32 +597,16 @@ impl<T: TransmuteFromObject> PyTupleTyped<T> {
560597
}
561598
}
562599

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> {
600+
impl<R: fmt::Debug> fmt::Debug for PyTupleTyped<R> {
573601
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
602+
self.tuple.as_slice().fmt(f)
582603
}
583604
}
584605

585-
impl<T: TransmuteFromObject> ToPyObject for PyTupleTyped<T> {
606+
impl<T: PyPayload> From<PyRef<PyTupleTyped<PyRef<T>>>> for PyTupleRef {
586607
#[inline]
587-
fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
588-
self.tuple.into()
608+
fn from(tup: PyRef<PyTupleTyped<PyRef<T>>>) -> Self {
609+
tup.into_untyped()
589610
}
590611
}
591612

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
}
@@ -1036,24 +1036,24 @@ impl Constructor for PyType {
10361036
// TODO: Flags is currently initialized with HAS_DICT. Should be
10371037
// updated when __slots__ are supported (toggling the flag off if
10381038
// a class has __slots__ defined).
1039-
let heaptype_slots: Option<PyTupleTyped<PyStrRef>> =
1039+
let heaptype_slots: Option<PyRef<PyTupleTyped<PyStrRef>>> =
10401040
if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
1041-
Some(if x.to_owned().class().is(vm.ctx.types.str_type) {
1042-
PyTupleTyped::<PyStrRef>::try_from_object(
1043-
vm,
1044-
vec![x.to_owned()].into_pytuple(vm).into(),
1045-
)?
1041+
let slots = if x.class().is(vm.ctx.types.str_type) {
1042+
let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
1043+
PyTupleTyped::new_ref(vec![x.to_owned()], &vm.ctx)
10461044
} else {
1047-
let iter = x.to_owned().get_iter(vm)?;
1045+
let iter = x.get_iter(vm)?;
10481046
let elements = {
10491047
let mut elements = Vec::new();
10501048
while let PyIterReturn::Return(element) = iter.next(vm)? {
10511049
elements.push(element);
10521050
}
10531051
elements
10541052
};
1055-
PyTupleTyped::<PyStrRef>::try_from_object(vm, elements.into_pytuple(vm).into())?
1056-
})
1053+
let tuple = elements.into_pytuple(vm);
1054+
tuple.try_into_typed(vm)?
1055+
};
1056+
Some(slots)
10571057
} else {
10581058
None
10591059
};
@@ -1077,7 +1077,7 @@ impl Constructor for PyType {
10771077
let heaptype_ext = HeapTypeExt {
10781078
name: PyRwLock::new(name),
10791079
qualname: PyRwLock::new(qualname),
1080-
slots: heaptype_slots.to_owned(),
1080+
slots: heaptype_slots.clone(),
10811081
sequence_methods: PySequenceMethods::default(),
10821082
mapping_methods: PyMappingMethods::default(),
10831083
};

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)