diff --git a/vm/src/builtins/function.rs b/vm/src/builtins/function.rs index 45917adcf..16cb3e420 100644 --- a/vm/src/builtins/function.rs +++ b/vm/src/builtins/function.rs @@ -2,8 +2,8 @@ mod jit; use super::{ - PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyStr, PyStrRef, PyTupleRef, PyType, - PyTypeRef, tuple::PyTupleTyped, + PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyStr, PyStrRef, PyTuple, PyTupleRef, + PyType, PyTypeRef, }; #[cfg(feature = "jit")] use crate::common::lock::OnceCell; @@ -31,7 +31,7 @@ pub struct PyFunction { code: PyRef, globals: PyDictRef, builtins: PyObjectRef, - closure: Option>>, + closure: Option>>, defaults_and_kwdefaults: PyMutex<(Option, Option)>, name: PyMutex, qualname: PyMutex, @@ -59,7 +59,7 @@ impl PyFunction { pub(crate) fn new( code: PyRef, globals: PyDictRef, - closure: Option>>, + closure: Option>>, defaults: Option, kw_only_defaults: Option, qualname: PyStrRef, diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 9f589547f..2c3255b24 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -3,7 +3,6 @@ use crate::common::{ hash::{PyHash, PyUHash}, lock::PyMutex, }; -use crate::object::{MaybeTraverse, Traverse, TraverseFn}; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, atomic_func, @@ -22,14 +21,14 @@ use crate::{ utils::collection_repr, vm::VirtualMachine, }; -use std::{fmt, marker::PhantomData, sync::LazyLock}; +use std::{fmt, sync::LazyLock}; #[pyclass(module = false, name = "tuple", traverse)] -pub struct PyTuple { - elements: Box<[PyObjectRef]>, +pub struct PyTuple { + elements: Box<[R]>, } -impl fmt::Debug for PyTuple { +impl fmt::Debug for PyTuple { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // TODO: implement more informational, non-recursive Debug formatter f.write_str("tuple") @@ -140,39 +139,60 @@ impl Constructor for PyTuple { } } -impl AsRef<[PyObjectRef]> for PyTuple { - fn as_ref(&self) -> &[PyObjectRef] { - self.as_slice() +impl AsRef<[R]> for PyTuple { + fn as_ref(&self) -> &[R] { + &self.elements } } -impl std::ops::Deref for PyTuple { - type Target = [PyObjectRef]; +impl std::ops::Deref for PyTuple { + type Target = [R]; - fn deref(&self) -> &[PyObjectRef] { - self.as_slice() + fn deref(&self) -> &[R] { + &self.elements } } -impl<'a> std::iter::IntoIterator for &'a PyTuple { - type Item = &'a PyObjectRef; - type IntoIter = std::slice::Iter<'a, PyObjectRef>; +impl<'a, R> std::iter::IntoIterator for &'a PyTuple { + type Item = &'a R; + type IntoIter = std::slice::Iter<'a, R>; fn into_iter(self) -> Self::IntoIter { self.iter() } } -impl<'a> std::iter::IntoIterator for &'a Py { - type Item = &'a PyObjectRef; - type IntoIter = std::slice::Iter<'a, PyObjectRef>; +impl<'a, R> std::iter::IntoIterator for &'a Py> { + type Item = &'a R; + type IntoIter = std::slice::Iter<'a, R>; fn into_iter(self) -> Self::IntoIter { self.iter() } } -impl PyTuple { +impl PyTuple { + pub const fn as_slice(&self) -> &[R] { + &self.elements + } + + #[inline] + pub fn len(&self) -> usize { + self.elements.len() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.elements.is_empty() + } + + #[inline] + pub fn iter(&self) -> std::slice::Iter<'_, R> { + self.elements.iter() + } +} + +impl PyTuple { pub fn new_ref(elements: Vec, ctx: &Context) -> PyRef { if elements.is_empty() { ctx.empty_tuple.clone() @@ -189,10 +209,6 @@ impl PyTuple { Self { elements } } - pub const fn as_slice(&self) -> &[PyObjectRef] { - &self.elements - } - fn repeat(zelf: PyRef, value: isize, vm: &VirtualMachine) -> PyResult> { Ok(if zelf.elements.is_empty() || value == 0 { vm.ctx.empty_tuple.clone() @@ -214,6 +230,18 @@ impl PyTuple { } } +impl PyTuple> { + pub fn new_ref_typed(elements: Vec>, ctx: &Context) -> PyRef>> { + // SAFETY: PyRef has the same layout as PyObjectRef + unsafe { + let elements: Vec = + std::mem::transmute::>, Vec>(elements); + let tuple = PyTuple::::new_ref(elements, ctx); + std::mem::transmute::, PyRef>>>(tuple) + } + } +} + #[pyclass( flags(BASETYPE), with( @@ -272,11 +300,6 @@ impl PyTuple { self.elements.len() } - #[inline] - pub const fn is_empty(&self) -> bool { - self.elements.is_empty() - } - #[pymethod(name = "__rmul__")] #[pymethod] fn __mul__(zelf: PyRef, value: ArgSize, vm: &VirtualMachine) -> PyResult> { @@ -449,21 +472,38 @@ impl Representable for PyTuple { } } -impl PyRef { +impl PyRef> { pub fn try_into_typed( self, vm: &VirtualMachine, - ) -> PyResult>>> { - PyRef::>>::try_from_untyped(self, vm) + ) -> PyResult>>> { + // Check that all elements are of the correct type + for elem in self.as_slice() { + as TransmuteFromObject>::check(vm, elem)?; + } + // SAFETY: We just verified all elements are of type T + Ok(unsafe { std::mem::transmute::, PyRef>>>(self) }) } - /// # Safety - /// - /// The caller must ensure that all elements in the tuple are valid instances - /// of type `T` before calling this method. This is typically verified by - /// calling `try_into_typed` first. - unsafe fn into_typed_unchecked(self) -> PyRef>> { - let obj: PyObjectRef = self.into(); - unsafe { obj.downcast_unchecked::>>() } +} + +impl PyRef>> { + pub fn into_untyped(self) -> PyRef { + // SAFETY: PyTuple> has the same layout as PyTuple + unsafe { std::mem::transmute::>>, PyRef>(self) } + } +} + +impl Py>> { + pub fn as_untyped(&self) -> &Py { + // SAFETY: PyTuple> has the same layout as PyTuple + unsafe { std::mem::transmute::<&Py>>, &Py>(self) } + } +} + +impl From>>> for PyTupleRef { + #[inline] + fn from(tup: PyRef>>) -> Self { + tup.into_untyped() } } @@ -518,101 +558,6 @@ pub(crate) fn init(context: &Context) { PyTupleIterator::extend_class(context, context.types.tuple_iterator_type); } -#[repr(transparent)] -pub struct PyTupleTyped { - // SAFETY INVARIANT: T must be repr(transparent) over PyObjectRef, and the - // elements must be logically valid when transmuted to T - tuple: PyTuple, - _marker: PhantomData, -} - -unsafe impl Traverse for PyTupleTyped -where - R: TransmuteFromObject, -{ - fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) { - self.tuple.traverse(tracer_fn); - } -} - -impl MaybeTraverse for PyTupleTyped { - const IS_TRACE: bool = true; - fn try_traverse(&self, tracer_fn: &mut TraverseFn<'_>) { - self.traverse(tracer_fn); - } -} - -impl PyTupleTyped> { - pub fn new_ref(elements: Vec>, ctx: &Context) -> PyRef { - // SAFETY: PyRef has the same layout as PyObjectRef - unsafe { - let elements: Vec = - std::mem::transmute::>, Vec>(elements); - let tuple = PyTuple::new_ref(elements, ctx); - tuple.into_typed_unchecked::() - } - } -} - -impl PyRef>> { - pub fn into_untyped(self) -> PyRef { - // SAFETY: PyTupleTyped is transparent over PyTuple - unsafe { std::mem::transmute::>>, PyRef>(self) } - } - - pub fn try_from_untyped(tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult { - // Check that all elements are of the correct type - for elem in tuple.as_slice() { - as TransmuteFromObject>::check(vm, elem)?; - } - // SAFETY: We just verified all elements are of type T, and PyTupleTyped has the same layout as PyTuple - Ok(unsafe { std::mem::transmute::, PyRef>>>(tuple) }) - } -} - -impl Py>> { - pub fn as_untyped(&self) -> &Py { - // SAFETY: PyTupleTyped is transparent over PyTuple - unsafe { std::mem::transmute::<&Py>>, &Py>(self) } - } -} - -impl AsRef<[PyRef]> for PyTupleTyped> { - fn as_ref(&self) -> &[PyRef] { - self.as_slice() - } -} - -impl PyTupleTyped> { - #[inline] - pub fn as_slice(&self) -> &[PyRef] { - unsafe { &*(self.tuple.as_slice() as *const [PyObjectRef] as *const [PyRef]) } - } - - #[inline] - pub fn len(&self) -> usize { - self.tuple.len() - } - - #[inline] - pub fn is_empty(&self) -> bool { - self.tuple.is_empty() - } -} - -impl fmt::Debug for PyTupleTyped { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.tuple.as_slice().fmt(f) - } -} - -impl From>>> for PyTupleRef { - #[inline] - fn from(tup: PyRef>>) -> Self { - tup.into_untyped() - } -} - pub(super) fn tuple_hash(elements: &[PyObjectRef], vm: &VirtualMachine) -> PyResult { #[cfg(target_pointer_width = "64")] const PRIME1: PyUHash = 11400714785074694791; diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index 5a8f853bf..f2a4fde3b 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -1,5 +1,5 @@ use super::{ - PyClassMethod, PyDictRef, PyList, PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak, + PyClassMethod, PyDictRef, PyList, PyStr, PyStrInterned, PyStrRef, PyTupleRef, PyWeak, mappingproxy::PyMappingProxy, object, union_, }; use crate::{ @@ -12,7 +12,7 @@ use crate::{ PyMemberDescriptor, }, function::PyCellRef, - tuple::{IntoPyTuple, PyTupleTyped}, + tuple::{IntoPyTuple, PyTuple}, }, class::{PyClassImpl, StaticType}, common::{ @@ -62,7 +62,7 @@ unsafe impl crate::object::Traverse for PyType { pub struct HeapTypeExt { pub name: PyRwLock, pub qualname: PyRwLock, - pub slots: Option>>, + pub slots: Option>>, pub sequence_methods: PySequenceMethods, pub mapping_methods: PyMappingMethods, } @@ -1041,11 +1041,11 @@ impl Constructor for PyType { // TODO: Flags is currently initialized with HAS_DICT. Should be // updated when __slots__ are supported (toggling the flag off if // a class has __slots__ defined). - let heaptype_slots: Option>> = + let heaptype_slots: Option>> = if let Some(x) = attributes.get(identifier!(vm, __slots__)) { let slots = if x.class().is(vm.ctx.types.str_type) { let x = unsafe { x.downcast_unchecked_ref::() }; - PyTupleTyped::new_ref(vec![x.to_owned()], &vm.ctx) + PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx) } else { let iter = x.get_iter(vm)?; let elements = { diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs index 4c673831e..670728815 100644 --- a/vm/src/vm/context.rs +++ b/vm/src/vm/context.rs @@ -11,7 +11,6 @@ use crate::{ }, getset::PyGetSet, object, pystr, - tuple::PyTupleTyped, type_::PyAttributes, }, class::{PyClassImpl, StaticType}, @@ -375,7 +374,7 @@ impl Context { } #[inline] - pub fn empty_tuple_typed(&self) -> &Py> { + pub fn empty_tuple_typed(&self) -> &Py> { let py: &Py = &self.empty_tuple; unsafe { std::mem::transmute(py) } } diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index dbfa2147b..498c7e39d 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -20,10 +20,7 @@ use crate::{ AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, builtins::{ PyBaseExceptionRef, PyDictRef, PyInt, PyList, PyModule, PyStr, PyStrInterned, PyStrRef, - PyTypeRef, - code::PyCode, - pystr::AsPyStr, - tuple::{PyTuple, PyTupleTyped}, + PyTypeRef, code::PyCode, pystr::AsPyStr, tuple::PyTuple, }, codecs::CodecsRegistry, common::{hash::HashSecret, lock::PyMutex, rc::PyRc}, @@ -609,7 +606,7 @@ impl VirtualMachine { pub fn import_from<'a>( &self, module_name: impl AsPyStr<'a>, - from_list: &Py>, + from_list: &Py>, level: usize, ) -> PyResult { let module_name = module_name.as_pystr(&self.ctx); @@ -619,7 +616,7 @@ impl VirtualMachine { fn import_inner( &self, module: &Py, - from_list: &Py>, + from_list: &Py>, level: usize, ) -> PyResult { // if the import inputs seem weird, e.g a package import or something, rather than just