Skip to content

Commit eef8890

Browse files
authored
Integrate PyTupleTyped into PyTuple (RustPython#5959)
* Fix derive(Traverse) * PyPayload::payload_type_of * PyTupleTyped as alias of PyTuple * Fully integrate PyTupleTyped into PyTuple
2 parents 36f4d30 + 6342ad4 commit eef8890

File tree

11 files changed

+170
-189
lines changed

11 files changed

+170
-189
lines changed

derive-impl/src/pytraverse.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,19 @@ pub(crate) fn impl_pytraverse(mut item: DeriveInput) -> Result<TokenStream> {
105105

106106
let ty = &item.ident;
107107

108+
// Add Traverse bound to all type parameters
109+
for param in &mut item.generics.params {
110+
if let syn::GenericParam::Type(type_param) = param {
111+
type_param
112+
.bounds
113+
.push(syn::parse_quote!(::rustpython_vm::object::Traverse));
114+
}
115+
}
116+
117+
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
118+
108119
let ret = quote! {
109-
unsafe impl ::rustpython_vm::object::Traverse for #ty {
120+
unsafe impl #impl_generics ::rustpython_vm::object::Traverse for #ty #ty_generics #where_clause {
110121
fn traverse(&self, tracer_fn: &mut ::rustpython_vm::object::TraverseFn) {
111122
#trace_code
112123
}

vm/src/builtins/function.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
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;
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<PyTuple<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<PyTuple<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: 86 additions & 117 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::{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,6 +472,41 @@ impl Representable for PyTuple {
449472
}
450473
}
451474

475+
impl PyRef<PyTuple<PyObjectRef>> {
476+
pub fn try_into_typed<T: PyPayload>(
477+
self,
478+
vm: &VirtualMachine,
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) }
493+
}
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()
507+
}
508+
}
509+
452510
#[pyclass(module = false, name = "tuple_iterator", traverse)]
453511
#[derive(Debug)]
454512
pub(crate) struct PyTupleIterator {
@@ -500,95 +558,6 @@ pub(crate) fn init(context: &Context) {
500558
PyTupleIterator::extend_class(context, context.types.tuple_iterator_type);
501559
}
502560

503-
pub struct PyTupleTyped<T: TransmuteFromObject> {
504-
// SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the
505-
// elements must be logically valid when transmuted to T
506-
tuple: PyTupleRef,
507-
_marker: PhantomData<Vec<T>>,
508-
}
509-
510-
unsafe impl<T> Traverse for PyTupleTyped<T>
511-
where
512-
T: TransmuteFromObject + Traverse,
513-
{
514-
fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
515-
self.tuple.traverse(tracer_fn);
516-
}
517-
}
518-
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-
})
530-
}
531-
}
532-
533-
impl<T: TransmuteFromObject> AsRef<[T]> for PyTupleTyped<T> {
534-
fn as_ref(&self) -> &[T] {
535-
self.as_slice()
536-
}
537-
}
538-
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,
544-
}
545-
}
546-
547-
#[inline]
548-
pub fn as_slice(&self) -> &[T] {
549-
unsafe { &*(self.tuple.as_slice() as *const [PyObjectRef] as *const [T]) }
550-
}
551-
552-
#[inline]
553-
pub fn len(&self) -> usize {
554-
self.tuple.len()
555-
}
556-
557-
#[inline]
558-
pub fn is_empty(&self) -> bool {
559-
self.tuple.is_empty()
560-
}
561-
}
562-
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> {
573-
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
582-
}
583-
}
584-
585-
impl<T: TransmuteFromObject> ToPyObject for PyTupleTyped<T> {
586-
#[inline]
587-
fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
588-
self.tuple.into()
589-
}
590-
}
591-
592561
pub(super) fn tuple_hash(elements: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<PyHash> {
593562
#[cfg(target_pointer_width = "64")]
594563
const PRIME1: PyUHash = 11400714785074694791;

0 commit comments

Comments
 (0)