diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 4181892b7..e00e899b5 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -485,7 +485,7 @@ where Self::#ident as _ } }; - const NON_ATOMIC_SLOTS: &[&str] = &["new", "as_buffer"]; + const NON_ATOMIC_SLOTS: &[&str] = &["as_buffer"]; if NON_ATOMIC_SLOTS.contains(&slot_name.as_str()) { quote! { slots.#slot_ident = Some(#into_func); diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index 146c60ee9..208ebd56c 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -2,7 +2,7 @@ use super::{float, IntoPyBool, PyByteArray, PyBytes, PyStr, PyStrRef, PyTypeRef} use crate::common::hash; use crate::format::FormatSpec; use crate::function::{OptionalArg, OptionalOption}; -use crate::slots::{Comparable, Hashable, PyComparisonOp}; +use crate::slots::{Comparable, Hashable, PyComparisonOp, SlotConstructor}; use crate::VirtualMachine; use crate::{bytesinner::PyBytesInner, byteslike::try_bytes_like}; use crate::{ @@ -235,7 +235,46 @@ fn inner_truediv(i1: &BigInt, i2: &BigInt, vm: &VirtualMachine) -> PyResult { } } -#[pyimpl(flags(BASETYPE), with(Comparable, Hashable))] +impl SlotConstructor for PyInt { + type Args = IntOptions; + + fn py_new(cls: PyTypeRef, options: IntOptions, vm: &VirtualMachine) -> PyResult { + let value = if let OptionalArg::Present(val) = options.val_options { + if let OptionalArg::Present(base) = options.base { + let base = vm + .to_index(&base)? + .as_bigint() + .to_u32() + .filter(|&v| v == 0 || (2..=36).contains(&v)) + .ok_or_else(|| { + vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_owned()) + })?; + try_int_radix(&val, base, vm) + } else { + let val = if cls.is(&vm.ctx.types.int_type) { + match val.downcast_exact::(vm) { + Ok(i) => { + return Ok(i.into_pyobject(vm)); + } + Err(val) => val, + } + } else { + val + }; + + try_int(&val, vm) + } + } else if let OptionalArg::Present(_) = options.base { + Err(vm.new_type_error("int() missing string argument".to_owned())) + } else { + Ok(Zero::zero()) + }?; + + Self::with_value(cls, value, vm).into_pyresult(vm) + } +} + +#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, SlotConstructor))] impl PyInt { fn with_value(cls: PyTypeRef, value: T, vm: &VirtualMachine) -> PyResult> where @@ -258,42 +297,6 @@ impl PyInt { &self.value } - #[pyslot] - fn tp_new(cls: PyTypeRef, options: IntOptions, vm: &VirtualMachine) -> PyResult> { - let value = if let OptionalArg::Present(val) = options.val_options { - if let OptionalArg::Present(base) = options.base { - let base = vm - .to_index(&base)? - .as_bigint() - .to_u32() - .filter(|&v| v == 0 || (2..=36).contains(&v)) - .ok_or_else(|| { - vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_owned()) - })?; - try_int_radix(&val, base, vm) - } else { - let val = if cls.is(&vm.ctx.types.int_type) { - match val.downcast_exact::(vm) { - Ok(i) => { - return Ok(i); - } - Err(val) => val, - } - } else { - val - }; - - try_int(&val, vm) - } - } else if let OptionalArg::Present(_) = options.base { - Err(vm.new_type_error("int() missing string argument".to_owned())) - } else { - Ok(Zero::zero()) - }?; - - Self::with_value(cls, value, vm) - } - #[inline] fn int_op(&self, other: PyObjectRef, op: F, vm: &VirtualMachine) -> PyArithmaticValue where @@ -735,7 +738,7 @@ impl Hashable for PyInt { } #[derive(FromArgs)] -struct IntOptions { +pub struct IntOptions { #[pyarg(positional, optional)] val_options: OptionalArg, #[pyarg(any, optional)] diff --git a/vm/src/builtins/pytype.rs b/vm/src/builtins/pytype.rs index 581bd0f68..df286c2c0 100644 --- a/vm/src/builtins/pytype.rs +++ b/vm/src/builtins/pytype.rs @@ -151,6 +151,17 @@ impl PyType { }}; } match name { + "__new__" => { + let func: slots::NewFunc = + |cls: PyTypeRef, mut args: FuncArgs, vm: &VirtualMachine| { + let new = vm + .get_attribute_opt(cls.as_object().clone(), "__new__")? + .unwrap(); + args.prepend_arg(cls.into_object()); + vm.invoke(&new, args) + }; + update_slot!(new, func); + } "__call__" => { let func: slots::GenericMethod = |zelf, args, vm| vm.call_special_method(zelf.clone(), "__call__", args); @@ -442,10 +453,9 @@ impl PyType { let winner = calculate_meta_class(metatype.clone(), &bases, vm)?; let metatype = if !winner.is(&metatype) { #[allow(clippy::redundant_clone)] // false positive - if let Some(ref tp_new) = winner.clone().slots.new { + if let Some(ref tp_new) = winner.clone().slots.new.load() { // Pass it to the winner - args.prepend_arg(winner.into_object()); - return tp_new(vm, args); + return tp_new(winner, args, vm); } winner } else { @@ -787,9 +797,8 @@ fn call_tp_new( return vm.invoke(&new_meth, args); } } - if let Some(tp_new) = cls.slots.new.as_ref() { - args.prepend_arg(subtype.into_object()); - return tp_new(vm, args); + if let Some(tp_new) = cls.slots.new.load() { + return tp_new(subtype, args, vm); } } unreachable!("Should be able to find a new slot somewhere in the mro") diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 4258efff2..f49a4a65a 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -34,11 +34,6 @@ macro_rules! py_class { py_class } }; - (@extract_slots($ctx:expr, $slots:expr, (slot new), $value:expr)) => { - $slots.new = Some( - $crate::function::IntoPyNativeFunc::into_func($value) - ); - }; (@extract_slots($ctx:expr, $slots:expr, (slot $slot_name:ident), $value:expr)) => { $slots.$slot_name.store(Some($value)); }; diff --git a/vm/src/slots.rs b/vm/src/slots.rs index 5baf6f971..00672d33e 100644 --- a/vm/src/slots.rs +++ b/vm/src/slots.rs @@ -1,8 +1,8 @@ use crate::buffer::PyBuffer; -use crate::builtins::pystr::PyStrRef; +use crate::builtins::{PyStrRef, PyTypeRef}; use crate::common::hash::PyHash; use crate::common::lock::PyRwLock; -use crate::function::{FuncArgs, OptionalArg, PyNativeFunc}; +use crate::function::{FromArgs, FuncArgs, OptionalArg}; use crate::utils::Either; use crate::VirtualMachine; use crate::{ @@ -45,6 +45,7 @@ impl Default for PyTpFlags { } pub(crate) type GenericMethod = fn(&PyObjectRef, FuncArgs, &VirtualMachine) -> PyResult; +pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult; pub(crate) type DelFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<()>; pub(crate) type DescrGetFunc = fn(PyObjectRef, Option, Option, &VirtualMachine) -> PyResult; @@ -108,7 +109,7 @@ pub struct PyTypeSlots { // tp_dictoffset // tp_init // tp_alloc - pub new: Option, + pub new: AtomicCell>, // tp_free // tp_is_gc // tp_bases @@ -134,6 +135,19 @@ impl std::fmt::Debug for PyTypeSlots { } } +#[pyimpl] +pub trait SlotConstructor: PyValue { + type Args: FromArgs; + + #[pyslot] + fn tp_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + let args: Self::Args = args.bind(vm)?; + Self::py_new(cls, args, vm) + } + + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult; +} + #[pyimpl] pub trait SlotDestructor: PyValue { #[pyslot]