__hash__ to slot_wrapper (#6480)

This commit is contained in:
Jeong, YunWon
2025-12-24 18:56:47 +09:00
committed by GitHub
parent 3d7e521acd
commit c4e77287d1
3 changed files with 67 additions and 14 deletions

View File

@@ -6,7 +6,8 @@ use crate::{
common::hash::PyHash,
function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue},
types::{
Callable, Comparable, GetDescriptor, Hashable, InitFunc, PyComparisonOp, Representable,
Callable, Comparable, GetDescriptor, HashFunc, Hashable, InitFunc, PyComparisonOp,
Representable,
},
};
use rustpython_common::lock::PyRwLock;
@@ -391,6 +392,44 @@ pub fn init(ctx: &Context) {
// PySlotWrapper - wrapper_descriptor
/// Type-erased slot function - mirrors CPython's void* d_wrapped
/// Each variant knows how to call the wrapped function with proper types
#[derive(Clone, Copy)]
pub enum SlotFunc {
Init(InitFunc),
Hash(HashFunc),
}
impl std::fmt::Debug for SlotFunc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SlotFunc::Init(_) => write!(f, "SlotFunc::Init(...)"),
SlotFunc::Hash(_) => write!(f, "SlotFunc::Hash(...)"),
}
}
}
impl SlotFunc {
/// Call the wrapped slot function with proper type handling
pub fn call(&self, obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
match self {
SlotFunc::Init(func) => {
func(obj, args, vm)?;
Ok(vm.ctx.none())
}
SlotFunc::Hash(func) => {
if !args.args.is_empty() || !args.kwargs.is_empty() {
return Err(
vm.new_type_error("__hash__() takes no arguments (1 given)".to_owned())
);
}
let hash = func(&obj, vm)?;
Ok(vm.ctx.new_int(hash).into())
}
}
}
}
/// wrapper_descriptor: wraps a slot function as a Python method
// = PyWrapperDescrObject
#[pyclass(name = "wrapper_descriptor", module = false)]
@@ -398,7 +437,7 @@ pub fn init(ctx: &Context) {
pub struct PySlotWrapper {
pub typ: &'static Py<PyType>,
pub name: &'static PyStrInterned,
pub wrapped: InitFunc,
pub wrapped: SlotFunc,
pub doc: Option<&'static str>,
}
@@ -430,7 +469,7 @@ impl Callable for PySlotWrapper {
type Args = FuncArgs;
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
// list.__init__(l, [1,2,3]) form
// list.__init__(l, [1,2,3]) form - first arg is self
let (obj, rest): (PyObjectRef, FuncArgs) = args.bind(vm)?;
if !obj.fast_isinstance(zelf.typ) {
@@ -442,8 +481,7 @@ impl Callable for PySlotWrapper {
)));
}
(zelf.wrapped)(obj, rest, vm)?;
Ok(vm.ctx.none())
zelf.wrapped.call(obj, rest, vm)
}
}
@@ -506,8 +544,7 @@ impl Callable for PyMethodWrapper {
type Args = FuncArgs;
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
(zelf.wrapper.wrapped)(zelf.obj.clone(), args, vm)?;
Ok(vm.ctx.none())
zelf.wrapper.wrapped.call(zelf.obj.clone(), args, vm)
}
}

View File

@@ -2,7 +2,10 @@
use crate::{
PyPayload,
builtins::{PyBaseObject, PyType, PyTypeRef, descriptor::PySlotWrapper},
builtins::{
PyBaseObject, PyType, PyTypeRef,
descriptor::{PySlotWrapper, SlotFunc},
},
function::PyMethodDef,
object::Py,
types::{PyTypeFlags, PyTypeSlots, hash_not_implemented},
@@ -143,13 +146,30 @@ pub trait PyClassImpl: PyClassDef {
let wrapper = PySlotWrapper {
typ: class,
name: ctx.intern_str("__init__"),
wrapped: init_func,
wrapped: SlotFunc::Init(init_func),
doc: Some("Initialize self. See help(type(self)) for accurate signature."),
};
class.set_attr(init_name, wrapper.into_ref(ctx).into());
}
}
// Add __hash__ slot wrapper if slot exists and not already in dict
// Note: hash_not_implemented is handled separately (sets __hash__ = None)
if let Some(hash_func) = class.slots.hash.load()
&& hash_func as usize != hash_not_implemented as usize
{
let hash_name = identifier!(ctx, __hash__);
if !class.attributes.read().contains_key(hash_name) {
let wrapper = PySlotWrapper {
typ: class,
name: ctx.intern_str("__hash__"),
wrapped: SlotFunc::Hash(hash_func),
doc: Some("Return hash(self)."),
};
class.set_attr(hash_name, wrapper.into_ref(ctx).into());
}
}
if class.slots.hash.load().map_or(0, |h| h as usize) == hash_not_implemented as usize {
class.set_attr(ctx.names.__hash__, ctx.none.clone().into());
}

View File

@@ -1098,11 +1098,7 @@ pub trait Hashable: PyPayload {
Self::hash(zelf, vm)
}
#[inline]
#[pymethod]
fn __hash__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyHash> {
Self::slot_hash(&zelf, vm)
}
// __hash__ is now exposed via SlotFunc::Hash wrapper in extend_class()
fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash>;
}