Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/capi/src/abstract_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ pub unsafe extern "C" fn PyVectorcall_Call(
Some(kwnames.as_slice())
};

callable.vectorcall(args, num_positional_args, kwnames, vm)
callable.vectorcall_call(args, num_positional_args, kwnames, vm)
})
}

Expand Down
5 changes: 5 additions & 0 deletions crates/derive-impl/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,11 @@ where
slots.#slot_ident.store(Some(Self::#ident as _));
slots.has_release_buffer.store(Self::RELEASE_BUFFER);
}
} else if slot_name == "vectorcall" {
quote_spanned! { span =>
slots.#slot_ident.store(Some(Self::#ident as _));
slots.flags.insert(::rustpython_vm::types::PyTypeFlags::HAVE_VECTORCALL);
}
} else if POINTER_SLOTS.contains(&slot_name.as_str()) {
quote_spanned! { span =>
slots.#slot_ident.store(Some(PointerSlot::from(Self::#ident())));
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ pub(crate) fn init(context: &'static Context) {
.types
.bool_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_bool));
}

Expand Down
6 changes: 6 additions & 0 deletions crates/vm/src/builtins/builtin_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,12 @@ pub(crate) fn init(context: &'static Context) {
.slots
.vectorcall
.store(Some(vectorcall_native_function));
context
.types
.builtin_function_or_method_type
.slots
.flags
.insert(crate::types::PyTypeFlags::HAVE_VECTORCALL);
PyNativeMethod::extend_class(context, context.types.builtin_method_type);
}

Expand Down
10 changes: 10 additions & 0 deletions crates/vm/src/builtins/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1658,13 +1658,23 @@ pub(crate) fn init(ctx: &'static Context) {
.slots
.vectorcall
.store(Some(vectorcall_method_descriptor));
ctx.types
.method_descriptor_type
.slots
.flags
.insert(crate::types::PyTypeFlags::HAVE_VECTORCALL);
PyClassMethodDescriptor::extend_class(ctx, ctx.types.classmethod_descriptor_type);
PyWrapper::extend_class(ctx, ctx.types.wrapper_descriptor_type);
ctx.types
.wrapper_descriptor_type
.slots
.vectorcall
.store(Some(vectorcall_wrapper));
ctx.types
.wrapper_descriptor_type
.slots
.flags
.insert(crate::types::PyTypeFlags::HAVE_VECTORCALL);
PyMethodWrapper::extend_class(ctx, ctx.types.method_wrapper_type);
}

Expand Down
4 changes: 2 additions & 2 deletions crates/vm/src/builtins/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2599,7 +2599,7 @@ pub(crate) fn init(context: &'static Context) {
.types
.dict_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_dict));
PyDictKeys::extend_class(context, context.types.dict_keys_type);
PyDictKeyIterator::extend_class(context, context.types.dict_keyiterator_type);
Expand All @@ -2615,7 +2615,7 @@ pub(crate) fn init(context: &'static Context) {
.types
.frozendict_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_frozendict));
}

Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,5 +585,5 @@ fn vectorcall_float(
#[rustfmt::skip] // to avoid line splitting
pub(crate) fn init(context: &'static Context) {
PyFloat::extend_class(context, context.types.float_type);
context.types.float_type.slots().vectorcall.store(Some(vectorcall_float));
context.types.float_type.slots().constructor_vectorcall.store(Some(vectorcall_float));
}
12 changes: 12 additions & 0 deletions crates/vm/src/builtins/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1876,6 +1876,12 @@ pub(crate) fn init(context: &'static Context) {
.slots
.vectorcall
.store(Some(vectorcall_function));
context
.types
.function_type
.slots
.flags
.insert(crate::types::PyTypeFlags::HAVE_VECTORCALL);

PyBoundMethod::extend_class(context, context.types.bound_method_type);
context
Expand All @@ -1884,6 +1890,12 @@ pub(crate) fn init(context: &'static Context) {
.slots
.vectorcall
.store(Some(vectorcall_bound_method));
context
.types
.bound_method_type
.slots
.flags
.insert(crate::types::PyTypeFlags::HAVE_VECTORCALL);

PyCell::extend_class(context, context.types.cell_type);
}
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,6 @@ pub(crate) fn init(context: &'static Context) {
.types
.int_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_int));
}
5 changes: 4 additions & 1 deletion crates/vm/src/builtins/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,10 @@ fn vectorcall_list(
pub(crate) fn init(context: &'static Context) {
let list_type = &context.types.list_type;
PyList::extend_class(context, list_type);
list_type.slots.vectorcall.store(Some(vectorcall_list));
list_type
.slots
.constructor_vectorcall
.store(Some(vectorcall_list));

PyListIterator::extend_class(context, context.types.list_iterator_type);
PyListReverseIterator::extend_class(context, context.types.list_reverseiterator_type);
Expand Down
4 changes: 2 additions & 2 deletions crates/vm/src/builtins/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1885,15 +1885,15 @@ pub(crate) fn init(context: &'static Context) {
.types
.set_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_set));

PyFrozenSet::extend_class(context, context.types.frozenset_type);
context
.types
.frozenset_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_frozenset));

PySetIterator::extend_class(context, context.types.set_iterator_type);
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1959,7 +1959,7 @@ pub(crate) fn init(ctx: &'static Context) {
ctx.types
.str_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_str));

PyStrIterator::extend_class(ctx, ctx.types.str_iterator_type);
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/builtins/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ pub(crate) fn init(context: &'static Context) {
.types
.tuple_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_tuple));
}

Expand Down
51 changes: 22 additions & 29 deletions crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use crate::{
protocol::{PyIterReturn, PyNumberMethods},
types::{
AsNumber, Callable, Constructor, GetAttr, Initializer, NewFunc, PyTypeFlags, PyTypeSlots,
Representable, SLOT_DEFS, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot, fn_addr,
new_wrapper,
Representable, SLOT_DEFS, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot,
VectorCallFunc, fn_addr, new_wrapper,
},
};
use core::{
Expand Down Expand Up @@ -3433,26 +3433,15 @@ fn subtype_get_weakref(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
* The magical type type
*/

/// Vectorcall for PyType (PEP 590).
/// Fast path: type(x) returns x.__class__ without constructing FuncArgs.
///
/// # Implementation note: `slots.vectorcall` dual use
///
/// CPython has three distinct fields on PyTypeObject:
/// - `tp_vectorcall`: constructor fast path (e.g. `list_vectorcall`)
/// - `tp_vectorcall_offset`: per-instance vectorcall for callables (e.g. functions)
/// - `tp_call`: standard call slot
///
/// RustPython collapses the first two into a single `slots.vectorcall`. The
/// `call.is_none()` guard below distinguishes the two uses: callable types have
/// `slots.call` set, so their `slots.vectorcall` is for calling instances,
/// not for construction.
///
/// This heuristic is correct for all current builtins but is not a general
/// solution — `type` itself is both callable and has a constructor vectorcall,
/// handled by the explicit `zelf.is(type_type)` check above the guard.
/// If more such types arise, consider splitting into a dedicated
/// `constructor_vectorcall` slot.
// Types can be visible to custom mro/__classcell__ code during initialization.
// A missing new slot or constructor implementation leaves ordinary call as fallback.
fn resolve_type_vectorcall(obj: &PyObject) -> Option<VectorCallFunc> {
let typ = obj.downcast_ref::<PyType>()?;
typ.slots.new.load()?;
typ.slots.constructor_vectorcall.load()
}

/// Vectorcall for type's own constructor (PEP 590).
fn vectorcall_type(
zelf_obj: &PyObject,
args: Vec<PyObjectRef>,
Expand All @@ -3468,12 +3457,6 @@ fn vectorcall_type(
if nargs == 1 && no_kwargs {
return Ok(args[0].obj_type());
}
} else if zelf.slots().call.load().is_none() && zelf.slots().new.load().is_some() {
// Per-type constructor vectorcall for non-callable types (dict, list, int, etc.)
// Also guard on slots.new to avoid dispatching for DISALLOW_INSTANTIATION types.
if let Some(type_vc) = zelf.slots().vectorcall.load() {
return type_vc(zelf_obj, args, nargs, kwnames, vm);
}
}

// Fallback: construct FuncArgs and use standard call
Expand All @@ -3486,8 +3469,18 @@ pub(crate) fn init(ctx: &'static Context) {
ctx.types
.type_type
.slots
.vectorcall
.constructor_vectorcall
.store(Some(vectorcall_type));
ctx.types
.type_type
.slots
.vectorcall_resolver
.store(Some(resolve_type_vectorcall));
ctx.types
.type_type
.slots
.flags
.insert(PyTypeFlags::HAVE_VECTORCALL);
}

pub(crate) fn call_slot_new(
Expand Down
7 changes: 5 additions & 2 deletions crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7410,7 +7410,8 @@ impl ExecutingFrame<'_> {
let nargs: u32 = arg.into();
let callable = self.nth_value(nargs + 1);
if let Some(cls) = callable.downcast_ref::<PyType>()
&& cls.slots().vectorcall.load().is_some()
&& cls.slots().constructor_vectorcall.load().is_some()
&& callable.vectorcall_func().is_some()
{
let (callable, args_vec) = self.take_call_args(nargs as usize);
let effective_nargs = args_vec.len();
Expand Down Expand Up @@ -11396,7 +11397,9 @@ impl ExecutingFrame<'_> {
return;
}
}
if cls.slots().vectorcall.load().is_some() {
if cls.slots().constructor_vectorcall.load().is_some()
&& callable.vectorcall_func().is_some()
{
self.specialize_at(instr_idx, cache_base, Instruction::CallBuiltinClass);
return;
}
Expand Down
Loading
Loading