Skip to content

Commit 6342ad4

Browse files
committed
Fully integrate PyTupleTyped into PyTuple
1 parent 14ce76e commit 6342ad4

File tree

5 files changed

+92
-151
lines changed

5 files changed

+92
-151
lines changed

vm/src/builtins/function.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
mod jit;
33

44
use super::{
5-
PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyStr, PyStrRef, PyTupleRef, PyType,
6-
PyTypeRef, tuple::PyTupleTyped,
5+
PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyStr, PyStrRef, PyTuple, PyTupleRef,
6+
PyType, PyTypeRef,
77
};
88
#[cfg(feature = "jit")]
99
use crate::common::lock::OnceCell;
@@ -31,7 +31,7 @@ pub struct PyFunction {
3131
code: PyRef<PyCode>,
3232
globals: PyDictRef,
3333
builtins: PyObjectRef,
34-
closure: Option<PyRef<PyTupleTyped<PyCellRef>>>,
34+
closure: Option<PyRef<PyTuple<PyCellRef>>>,
3535
defaults_and_kwdefaults: PyMutex<(Option<PyTupleRef>, Option<PyDictRef>)>,
3636
name: PyMutex<PyStrRef>,
3737
qualname: PyMutex<PyStrRef>,
@@ -59,7 +59,7 @@ impl PyFunction {
5959
pub(crate) fn new(
6060
code: PyRef<PyCode>,
6161
globals: PyDictRef,
62-
closure: Option<PyRef<PyTupleTyped<PyCellRef>>>,
62+
closure: Option<PyRef<PyTuple<PyCellRef>>>,
6363
defaults: Option<PyTupleRef>,
6464
kw_only_defaults: Option<PyDictRef>,
6565
qualname: PyStrRef,

vm/src/builtins/tuple.rs

Lines changed: 79 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::common::{
33
hash::{PyHash, PyUHash},
44
lock::PyMutex,
55
};
6-
use crate::object::{MaybeTraverse, Traverse, TraverseFn};
76
use crate::{
87
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
98
atomic_func,
@@ -22,14 +21,14 @@ use crate::{
2221
utils::collection_repr,
2322
vm::VirtualMachine,
2423
};
25-
use std::{fmt, marker::PhantomData, sync::LazyLock};
24+
use std::{fmt, sync::LazyLock};
2625

2726
#[pyclass(module = false, name = "tuple", traverse)]
28-
pub struct PyTuple {
29-
elements: Box<[PyObjectRef]>,
27+
pub struct PyTuple<R = PyObjectRef> {
28+
elements: Box<[R]>,
3029
}
3130

32-
impl fmt::Debug for PyTuple {
31+
impl<R> fmt::Debug for PyTuple<R> {
3332
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3433
// TODO: implement more informational, non-recursive Debug formatter
3534
f.write_str("tuple")
@@ -140,39 +139,60 @@ impl Constructor for PyTuple {
140139
}
141140
}
142141

143-
impl AsRef<[PyObjectRef]> for PyTuple {
144-
fn as_ref(&self) -> &[PyObjectRef] {
145-
self.as_slice()
142+
impl<R> AsRef<[R]> for PyTuple<R> {
143+
fn as_ref(&self) -> &[R] {
144+
&self.elements
146145
}
147146
}
148147

149-
impl std::ops::Deref for PyTuple {
150-
type Target = [PyObjectRef];
148+
impl<R> std::ops::Deref for PyTuple<R> {
149+
type Target = [R];
151150

152-
fn deref(&self) -> &[PyObjectRef] {
153-
self.as_slice()
151+
fn deref(&self) -> &[R] {
152+
&self.elements
154153
}
155154
}
156155

157-
impl<'a> std::iter::IntoIterator for &'a PyTuple {
158-
type Item = &'a PyObjectRef;
159-
type IntoIter = std::slice::Iter<'a, PyObjectRef>;
156+
impl<'a, R> std::iter::IntoIterator for &'a PyTuple<R> {
157+
type Item = &'a R;
158+
type IntoIter = std::slice::Iter<'a, R>;
160159

161160
fn into_iter(self) -> Self::IntoIter {
162161
self.iter()
163162
}
164163
}
165164

166-
impl<'a> std::iter::IntoIterator for &'a Py<PyTuple> {
167-
type Item = &'a PyObjectRef;
168-
type IntoIter = std::slice::Iter<'a, PyObjectRef>;
165+
impl<'a, R> std::iter::IntoIterator for &'a Py<PyTuple<R>> {
166+
type Item = &'a R;
167+
type IntoIter = std::slice::Iter<'a, R>;
169168

170169
fn into_iter(self) -> Self::IntoIter {
171170
self.iter()
172171
}
173172
}
174173

175-
impl PyTuple {
174+
impl<R> PyTuple<R> {
175+
pub const fn as_slice(&self) -> &[R] {
176+
&self.elements
177+
}
178+
179+
#[inline]
180+
pub fn len(&self) -> usize {
181+
self.elements.len()
182+
}
183+
184+
#[inline]
185+
pub fn is_empty(&self) -> bool {
186+
self.elements.is_empty()
187+
}
188+
189+
#[inline]
190+
pub fn iter(&self) -> std::slice::Iter<'_, R> {
191+
self.elements.iter()
192+
}
193+
}
194+
195+
impl PyTuple<PyObjectRef> {
176196
pub fn new_ref(elements: Vec<PyObjectRef>, ctx: &Context) -> PyRef<Self> {
177197
if elements.is_empty() {
178198
ctx.empty_tuple.clone()
@@ -189,10 +209,6 @@ impl PyTuple {
189209
Self { elements }
190210
}
191211

192-
pub const fn as_slice(&self) -> &[PyObjectRef] {
193-
&self.elements
194-
}
195-
196212
fn repeat(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
197213
Ok(if zelf.elements.is_empty() || value == 0 {
198214
vm.ctx.empty_tuple.clone()
@@ -214,6 +230,18 @@ impl PyTuple {
214230
}
215231
}
216232

233+
impl<T> PyTuple<PyRef<T>> {
234+
pub fn new_ref_typed(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<PyTuple<PyRef<T>>> {
235+
// SAFETY: PyRef<T> has the same layout as PyObjectRef
236+
unsafe {
237+
let elements: Vec<PyObjectRef> =
238+
std::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
239+
let tuple = PyTuple::<PyObjectRef>::new_ref(elements, ctx);
240+
std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTuple<PyRef<T>>>>(tuple)
241+
}
242+
}
243+
}
244+
217245
#[pyclass(
218246
flags(BASETYPE),
219247
with(
@@ -272,11 +300,6 @@ impl PyTuple {
272300
self.elements.len()
273301
}
274302

275-
#[inline]
276-
pub const fn is_empty(&self) -> bool {
277-
self.elements.is_empty()
278-
}
279-
280303
#[pymethod(name = "__rmul__")]
281304
#[pymethod]
282305
fn __mul__(zelf: PyRef<Self>, value: ArgSize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
@@ -449,21 +472,38 @@ impl Representable for PyTuple {
449472
}
450473
}
451474

452-
impl PyRef<PyTuple> {
475+
impl PyRef<PyTuple<PyObjectRef>> {
453476
pub fn try_into_typed<T: PyPayload>(
454477
self,
455478
vm: &VirtualMachine,
456-
) -> PyResult<PyRef<PyTupleTyped<PyRef<T>>>> {
457-
PyRef::<PyTupleTyped<PyRef<T>>>::try_from_untyped(self, vm)
479+
) -> PyResult<PyRef<PyTuple<PyRef<T>>>> {
480+
// Check that all elements are of the correct type
481+
for elem in self.as_slice() {
482+
<PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
483+
}
484+
// SAFETY: We just verified all elements are of type T
485+
Ok(unsafe { std::mem::transmute::<PyRef<PyTuple>, PyRef<PyTuple<PyRef<T>>>>(self) })
486+
}
487+
}
488+
489+
impl<T: PyPayload> PyRef<PyTuple<PyRef<T>>> {
490+
pub fn into_untyped(self) -> PyRef<PyTuple> {
491+
// SAFETY: PyTuple<PyRef<T>> has the same layout as PyTuple
492+
unsafe { std::mem::transmute::<PyRef<PyTuple<PyRef<T>>>, PyRef<PyTuple>>(self) }
458493
}
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>>>() }
494+
}
495+
496+
impl<T: PyPayload> Py<PyTuple<PyRef<T>>> {
497+
pub fn as_untyped(&self) -> &Py<PyTuple> {
498+
// SAFETY: PyTuple<PyRef<T>> has the same layout as PyTuple
499+
unsafe { std::mem::transmute::<&Py<PyTuple<PyRef<T>>>, &Py<PyTuple>>(self) }
500+
}
501+
}
502+
503+
impl<T: PyPayload> From<PyRef<PyTuple<PyRef<T>>>> for PyTupleRef {
504+
#[inline]
505+
fn from(tup: PyRef<PyTuple<PyRef<T>>>) -> Self {
506+
tup.into_untyped()
467507
}
468508
}
469509

@@ -518,101 +558,6 @@ pub(crate) fn init(context: &Context) {
518558
PyTupleIterator::extend_class(context, context.types.tuple_iterator_type);
519559
}
520560

521-
#[repr(transparent)]
522-
pub struct PyTupleTyped<R> {
523-
// SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the
524-
// elements must be logically valid when transmuted to T
525-
tuple: PyTuple,
526-
_marker: PhantomData<R>,
527-
}
528-
529-
unsafe impl<R> Traverse for PyTupleTyped<R>
530-
where
531-
R: TransmuteFromObject,
532-
{
533-
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
534-
self.tuple.traverse(tracer_fn);
535-
}
536-
}
537-
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);
542-
}
543-
}
544-
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-
}
554-
}
555-
}
556-
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)?;
567-
}
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) })
570-
}
571-
}
572-
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>> {
587-
#[inline]
588-
pub fn as_slice(&self) -> &[PyRef<T>] {
589-
unsafe { &*(self.tuple.as_slice() as *const [PyObjectRef] as *const [PyRef<T>]) }
590-
}
591-
592-
#[inline]
593-
pub fn len(&self) -> usize {
594-
self.tuple.len()
595-
}
596-
597-
#[inline]
598-
pub fn is_empty(&self) -> bool {
599-
self.tuple.is_empty()
600-
}
601-
}
602-
603-
impl<R: fmt::Debug> fmt::Debug for PyTupleTyped<R> {
604-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605-
self.tuple.as_slice().fmt(f)
606-
}
607-
}
608-
609-
impl<T: PyPayload> From<PyRef<PyTupleTyped<PyRef<T>>>> for PyTupleRef {
610-
#[inline]
611-
fn from(tup: PyRef<PyTupleTyped<PyRef<T>>>) -> Self {
612-
tup.into_untyped()
613-
}
614-
}
615-
616561
pub(super) fn tuple_hash(elements: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<PyHash> {
617562
#[cfg(target_pointer_width = "64")]
618563
const PRIME1: PyUHash = 11400714785074694791;

vm/src/builtins/type.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{
2-
PyClassMethod, PyDictRef, PyList, PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak,
2+
PyClassMethod, PyDictRef, PyList, PyStr, PyStrInterned, PyStrRef, PyTupleRef, PyWeak,
33
mappingproxy::PyMappingProxy, object, union_,
44
};
55
use crate::{
@@ -12,7 +12,7 @@ use crate::{
1212
PyMemberDescriptor,
1313
},
1414
function::PyCellRef,
15-
tuple::{IntoPyTuple, PyTupleTyped},
15+
tuple::{IntoPyTuple, PyTuple},
1616
},
1717
class::{PyClassImpl, StaticType},
1818
common::{
@@ -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<PyRef<PyTupleTyped<PyStrRef>>>,
65+
pub slots: Option<PyRef<PyTuple<PyStrRef>>>,
6666
pub sequence_methods: PySequenceMethods,
6767
pub mapping_methods: PyMappingMethods,
6868
}
@@ -1041,11 +1041,11 @@ 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<PyRef<PyTupleTyped<PyStrRef>>> =
1044+
let heaptype_slots: Option<PyRef<PyTuple<PyStrRef>>> =
10451045
if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
10461046
let slots = if x.class().is(vm.ctx.types.str_type) {
10471047
let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
1048-
PyTupleTyped::new_ref(vec![x.to_owned()], &vm.ctx)
1048+
PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx)
10491049
} else {
10501050
let iter = x.get_iter(vm)?;
10511051
let elements = {

vm/src/vm/context.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use crate::{
1111
},
1212
getset::PyGetSet,
1313
object, pystr,
14-
tuple::PyTupleTyped,
1514
type_::PyAttributes,
1615
},
1716
class::{PyClassImpl, StaticType},
@@ -375,7 +374,7 @@ impl Context {
375374
}
376375

377376
#[inline]
378-
pub fn empty_tuple_typed<T>(&self) -> &Py<PyTupleTyped<T>> {
377+
pub fn empty_tuple_typed<T>(&self) -> &Py<PyTuple<T>> {
379378
let py: &Py<PyTuple> = &self.empty_tuple;
380379
unsafe { std::mem::transmute(py) }
381380
}

0 commit comments

Comments
 (0)