diff --git a/crates/derive-impl/src/pyclass.rs b/crates/derive-impl/src/pyclass.rs index 809d3164b4a..175649b1173 100644 --- a/crates/derive-impl/src/pyclass.rs +++ b/crates/derive-impl/src/pyclass.rs @@ -1178,6 +1178,12 @@ where quote_spanned! { span => slots.#slot_ident = Self::#ident().into(); } + } else if slot_name == "new" { + quote_spanned! { span => + slots.#slot_ident.store(Some(::rustpython_vm::types::NewFunc::Rust( + Self::#ident as _ + ))); + } } else { quote_spanned! { span => slots.#slot_ident.store(Some(Self::#ident as _)); @@ -1896,7 +1902,9 @@ fn extract_impl_attrs(attr: PunctuatedNestedMeta, item: &Ident) -> Result - slots.new.store(Some(::slot_new as _)); + slots.new.store(Some(::rustpython_vm::types::NewFunc::Rust( + ::slot_new as _ + ))); } } else { quote_spanned! { item_span => diff --git a/crates/vm/src/builtins/bool.rs b/crates/vm/src/builtins/bool.rs index 4bb980d71a2..adbab4897ea 100644 --- a/crates/vm/src/builtins/bool.rs +++ b/crates/vm/src/builtins/bool.rs @@ -189,7 +189,11 @@ fn vectorcall_bool( ) -> PyResult { let zelf: &Py = zelf_obj.downcast_ref().unwrap(); let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames); - (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm) + zelf.slots + .new + .load() + .unwrap() + .invoke(zelf.to_owned(), func_args, vm) } pub(crate) fn init(context: &'static Context) { diff --git a/crates/vm/src/builtins/float.rs b/crates/vm/src/builtins/float.rs index 1c861b14fc6..418a9fdc06e 100644 --- a/crates/vm/src/builtins/float.rs +++ b/crates/vm/src/builtins/float.rs @@ -552,7 +552,11 @@ fn vectorcall_float( ) -> PyResult { let zelf: &Py = zelf_obj.downcast_ref().unwrap(); let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames); - (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm) + zelf.slots + .new + .load() + .unwrap() + .invoke(zelf.to_owned(), func_args, vm) } #[rustfmt::skip] // to avoid line splitting diff --git a/crates/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs index 278a9cecbb1..d2352d8b39a 100644 --- a/crates/vm/src/builtins/int.rs +++ b/crates/vm/src/builtins/int.rs @@ -840,7 +840,11 @@ fn vectorcall_int( ) -> PyResult { let zelf: &Py = zelf_obj.downcast_ref().unwrap(); let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames); - (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm) + zelf.slots + .new + .load() + .unwrap() + .invoke(zelf.to_owned(), func_args, vm) } pub(crate) fn init(context: &'static Context) { diff --git a/crates/vm/src/builtins/set.rs b/crates/vm/src/builtins/set.rs index 1481bc1b391..5ea33e84c09 100644 --- a/crates/vm/src/builtins/set.rs +++ b/crates/vm/src/builtins/set.rs @@ -1464,7 +1464,11 @@ fn vectorcall_frozenset( ) -> PyResult { let zelf: &Py = zelf_obj.downcast_ref().unwrap(); let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames); - (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm) + zelf.slots + .new + .load() + .unwrap() + .invoke(zelf.to_owned(), func_args, vm) } pub(crate) fn init(context: &'static Context) { diff --git a/crates/vm/src/builtins/str.rs b/crates/vm/src/builtins/str.rs index b1c2c41973b..ad61601ef75 100644 --- a/crates/vm/src/builtins/str.rs +++ b/crates/vm/src/builtins/str.rs @@ -1778,7 +1778,11 @@ fn vectorcall_str( ) -> PyResult { let zelf: &Py = zelf_obj.downcast_ref().unwrap(); let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames); - (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm) + zelf.slots + .new + .load() + .unwrap() + .invoke(zelf.to_owned(), func_args, vm) } pub(crate) fn init(ctx: &'static Context) { diff --git a/crates/vm/src/builtins/tuple.rs b/crates/vm/src/builtins/tuple.rs index d48639b2c11..fe8c3f77e55 100644 --- a/crates/vm/src/builtins/tuple.rs +++ b/crates/vm/src/builtins/tuple.rs @@ -665,7 +665,11 @@ fn vectorcall_tuple( let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames); // Use the type's own slot_new rather than calling PyTuple::slot_new directly, // so Rust-level subclasses (e.g. struct sequences) get their custom slot_new called. - (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm) + zelf.slots + .new + .load() + .unwrap() + .invoke(zelf.to_owned(), func_args, vm) } pub(crate) fn init(context: &'static Context) { diff --git a/crates/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs index c27c6475f42..6b1e78c9429 100644 --- a/crates/vm/src/builtins/type.rs +++ b/crates/vm/src/builtins/type.rs @@ -2133,7 +2133,7 @@ impl Constructor for PyType { let metatype = if !winner.is(&metatype) { if let Some(ref slot_new) = winner.slots.new.load() { // Pass it to the winner - return slot_new(winner, args, vm); + return slot_new.invoke(winner, args, vm); } winner } else { @@ -2832,14 +2832,14 @@ impl Callable for PyType { // path incorrectly. if zelf.slots.init.load().is_none() && !zelf.is(vm.ctx.types.type_type) - && slot_new as usize != crate::types::new_wrapper as crate::types::NewFunc as usize + && slot_new.identity() != crate::types::new_wrapper as *const () as usize { - return slot_new(zelf.to_owned(), args, vm); + return slot_new.invoke(zelf.to_owned(), args, vm); } args.clone() }; - let obj = slot_new(zelf.to_owned(), args, vm)?; + let obj = slot_new.invoke(zelf.to_owned(), args, vm)?; if !obj.class().fast_issubclass(zelf) { return Ok(obj); @@ -3069,7 +3069,7 @@ pub(crate) fn call_slot_new( // Check if staticbase's tp_new differs from typ's tp_new let typ_new = typ.slots.new.load(); let staticbase_new = staticbase.slots.new.load(); - if typ_new.map(|f| f as usize) != staticbase_new.map(|f| f as usize) { + if typ_new.map(|f| f.identity()) != staticbase_new.map(|f| f.identity()) { return Err(vm.new_type_error(format!( "{}.__new__({}) is not safe, use {}.__new__()", typ.slot_name(), @@ -3083,7 +3083,7 @@ pub(crate) fn call_slot_new( .new .load() .expect("Should be able to find a new slot somewhere in the mro"); - slot_new(subtype, args, vm) + slot_new.invoke(subtype, args, vm) } pub(crate) fn or_(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { diff --git a/crates/vm/src/class.rs b/crates/vm/src/class.rs index 364ae721159..e23d41fc362 100644 --- a/crates/vm/src/class.rs +++ b/crates/vm/src/class.rs @@ -205,7 +205,7 @@ pub trait PyClassImpl: PyClassDef { let object_new = ctx.types.object_type.slots.new.load(); let is_object_itself = core::ptr::eq(class, ctx.types.object_type); let is_inherited_from_object = !is_object_itself - && object_new.is_some_and(|obj_new| slot_new as usize == obj_new as usize); + && object_new.is_some_and(|obj_new| slot_new.identity() == obj_new.identity()); if !is_inherited_from_object { let bound_new = diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index ae7f2c06164..9bb0299d9ff 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -9240,7 +9240,7 @@ impl ExecutingFrame<'_> { let cls_alloc = cls.slots.alloc.load(); if let (Some(cls_new_fn), Some(obj_new_fn), Some(cls_alloc_fn), Some(obj_alloc_fn)) = (cls_new, object_new, cls_alloc, object_alloc) - && cls_new_fn as usize == obj_new_fn as usize + && cls_new_fn.identity() == obj_new_fn.identity() && cls_alloc_fn as usize == obj_alloc_fn as usize { if type_version == 0 { diff --git a/crates/vm/src/types/slot.rs b/crates/vm/src/types/slot.rs index a5d7a41d3fd..94873f379da 100644 --- a/crates/vm/src/types/slot.rs +++ b/crates/vm/src/types/slot.rs @@ -310,10 +310,39 @@ pub(crate) type DescrGetFunc = pub(crate) type DescrSetFunc = fn(&PyObject, PyObjectRef, PySetterValue, &VirtualMachine) -> PyResult<()>; pub(crate) type AllocFunc = fn(PyTypeRef, usize, &VirtualMachine) -> PyResult; -pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult; pub(crate) type InitFunc = fn(PyObjectRef, FuncArgs, &VirtualMachine) -> PyResult<()>; pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>; +#[derive(Debug, Clone, Copy)] +pub enum NewFunc { + Rust(fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult), + C(unsafe extern "C" fn(*mut PyObject, *mut PyObject, *mut PyObject) -> *mut PyObject), +} + +impl NewFunc { + #[must_use] + pub fn identity(self) -> usize { + match self { + Self::Rust(func) => func as usize, + Self::C(func) => func as usize, + } + } + + pub fn invoke(self, cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + match self { + Self::Rust(func) => func(cls, args, vm), + Self::C(func) => { + todo!( + "call C new function: {:?} with args: {:?} {:?}", + func, + cls, + args + ) + } + } + } +} + // Sequence sub-slot function types pub(crate) type SeqLenFunc = fn(PySequence<'_>, &VirtualMachine) -> PyResult; pub(crate) type SeqConcatFunc = fn(PySequence<'_>, &PyObject, &VirtualMachine) -> PyResult; @@ -877,12 +906,12 @@ impl PyType { .iter() .find(|cls| cls.attributes.read().contains_key(name)) .is_some_and(|cls| { - cls.slots.new.load().map(|f| f as usize) - == Some(new_wrapper as NewFunc as usize) + cls.slots.new.load().map(NewFunc::identity) + == Some(NewFunc::Rust(new_wrapper as _).identity()) }) }; if needs_wrapper { - self.slots.new.store(Some(new_wrapper)); + self.slots.new.store(Some(NewFunc::Rust(new_wrapper as _))); self.slots.vectorcall.store(None); } else { let inherited = self.base.deref().and_then(|base| base.slots.new.load());