Skip to content

Commit 3c666d6

Browse files
committed
Add per-type vectorcall for builtin constructors
Add vectorcall fast paths for dict, list, set, int, float, str, bool, tuple, frozenset. Clear vectorcall when __init__/__new__ is overridden in Python. Prevent constructor vectorcall inheritance to heap subclasses. Fix stat_result to use struct_sequence_new with reference-copy for hidden time fields.
1 parent 0768cf8 commit 3c666d6

File tree

13 files changed

+218
-27
lines changed

13 files changed

+218
-27
lines changed

crates/vm/src/builtins/bool.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,26 @@ impl Representable for PyBool {
182182
}
183183
}
184184

185+
fn vectorcall_bool(
186+
zelf_obj: &PyObject,
187+
args: Vec<PyObjectRef>,
188+
nargs: usize,
189+
kwnames: Option<&[PyObjectRef]>,
190+
vm: &VirtualMachine,
191+
) -> PyResult {
192+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
193+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
194+
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
195+
}
196+
185197
pub(crate) fn init(context: &'static Context) {
186198
PyBool::extend_class(context, context.types.bool_type);
199+
context
200+
.types
201+
.bool_type
202+
.slots
203+
.vectorcall
204+
.store(Some(vectorcall_bool));
187205
}
188206

189207
// pub fn not(vm: &VirtualMachine, obj: &PyObject) -> PyResult<bool> {

crates/vm/src/builtins/bytearray.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ use crate::{
2323
},
2424
convert::{ToPyObject, ToPyResult},
2525
function::{
26-
ArgBytesLike, ArgIterable, ArgSize, Either, OptionalArg, OptionalOption, PyComparisonValue,
26+
ArgBytesLike, ArgIterable, ArgSize, Either, OptionalArg, OptionalOption,
27+
PyComparisonValue,
2728
},
2829
protocol::{
2930
BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, PyIterReturn,

crates/vm/src/builtins/dict.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ use crate::{
1515
class::{PyClassDef, PyClassImpl},
1616
common::ascii,
1717
dict_inner::{self, DictKey},
18-
function::{ArgIterable, KwArgs, OptionalArg, PyArithmeticValue::*, PyComparisonValue},
18+
function::{
19+
ArgIterable, FuncArgs, KwArgs, OptionalArg, PyArithmeticValue::*, PyComparisonValue,
20+
},
1921
iter::PyExactSizeIterator,
2022
protocol::{PyIterIter, PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods},
2123
recursion::ReprGuard,
@@ -1433,8 +1435,28 @@ fn set_inner_number_or(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyRes
14331435
set_inner_number_op(a, b, |a, b| a.union(b, vm), vm)
14341436
}
14351437

1438+
fn vectorcall_dict(
1439+
zelf_obj: &PyObject,
1440+
args: Vec<PyObjectRef>,
1441+
nargs: usize,
1442+
kwnames: Option<&[PyObjectRef]>,
1443+
vm: &VirtualMachine,
1444+
) -> PyResult {
1445+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1446+
let obj = PyDict::default().into_ref_with_type(vm, zelf.to_owned())?;
1447+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1448+
PyDict::slot_init(obj.clone().into(), func_args, vm)?;
1449+
Ok(obj.into())
1450+
}
1451+
14361452
pub(crate) fn init(context: &'static Context) {
14371453
PyDict::extend_class(context, context.types.dict_type);
1454+
context
1455+
.types
1456+
.dict_type
1457+
.slots
1458+
.vectorcall
1459+
.store(Some(vectorcall_dict));
14381460
PyDictKeys::extend_class(context, context.types.dict_keys_type);
14391461
PyDictKeyIterator::extend_class(context, context.types.dict_keyiterator_type);
14401462
PyDictReverseKeyIterator::extend_class(context, context.types.dict_reversekeyiterator_type);

crates/vm/src/builtins/float.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,20 @@ pub(crate) fn get_value(obj: &PyObject) -> f64 {
525525
obj.downcast_ref::<PyFloat>().unwrap().value
526526
}
527527

528+
fn vectorcall_float(
529+
zelf_obj: &PyObject,
530+
args: Vec<PyObjectRef>,
531+
nargs: usize,
532+
kwnames: Option<&[PyObjectRef]>,
533+
vm: &VirtualMachine,
534+
) -> PyResult {
535+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
536+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
537+
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
538+
}
539+
528540
#[rustfmt::skip] // to avoid line splitting
529541
pub fn init(context: &'static Context) {
530542
PyFloat::extend_class(context, context.types.float_type);
543+
context.types.float_type.slots.vectorcall.store(Some(vectorcall_float));
531544
}

crates/vm/src/builtins/int.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,24 @@ pub fn try_to_float(int: &BigInt, vm: &VirtualMachine) -> PyResult<f64> {
791791
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float"))
792792
}
793793

794+
fn vectorcall_int(
795+
zelf_obj: &PyObject,
796+
args: Vec<PyObjectRef>,
797+
nargs: usize,
798+
kwnames: Option<&[PyObjectRef]>,
799+
vm: &VirtualMachine,
800+
) -> PyResult {
801+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
802+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
803+
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
804+
}
805+
794806
pub(crate) fn init(context: &'static Context) {
795807
PyInt::extend_class(context, context.types.int_type);
808+
context
809+
.types
810+
.int_type
811+
.slots
812+
.vectorcall
813+
.store(Some(vectorcall_int));
796814
}

crates/vm/src/builtins/list.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,9 +768,24 @@ impl IterNext for PyListReverseIterator {
768768
}
769769
}
770770

771+
fn vectorcall_list(
772+
zelf_obj: &PyObject,
773+
args: Vec<PyObjectRef>,
774+
nargs: usize,
775+
kwnames: Option<&[PyObjectRef]>,
776+
vm: &VirtualMachine,
777+
) -> PyResult {
778+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
779+
let obj = PyList::default().into_ref_with_type(vm, zelf.to_owned())?;
780+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
781+
PyList::slot_init(obj.clone().into(), func_args, vm)?;
782+
Ok(obj.into())
783+
}
784+
771785
pub fn init(context: &'static Context) {
772786
let list_type = &context.types.list_type;
773787
PyList::extend_class(context, list_type);
788+
list_type.slots.vectorcall.store(Some(vectorcall_list));
774789

775790
PyListIterator::extend_class(context, context.types.list_iterator_type);
776791
PyListReverseIterator::extend_class(context, context.types.list_reverseiterator_type);

crates/vm/src/builtins/set.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,8 +1421,48 @@ impl IterNext for PySetIterator {
14211421
}
14221422
}
14231423

1424+
fn vectorcall_set(
1425+
zelf_obj: &PyObject,
1426+
args: Vec<PyObjectRef>,
1427+
nargs: usize,
1428+
kwnames: Option<&[PyObjectRef]>,
1429+
vm: &VirtualMachine,
1430+
) -> PyResult {
1431+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1432+
let obj = PySet::default().into_ref_with_type(vm, zelf.to_owned())?;
1433+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1434+
PySet::slot_init(obj.clone().into(), func_args, vm)?;
1435+
Ok(obj.into())
1436+
}
1437+
1438+
fn vectorcall_frozenset(
1439+
zelf_obj: &PyObject,
1440+
args: Vec<PyObjectRef>,
1441+
nargs: usize,
1442+
kwnames: Option<&[PyObjectRef]>,
1443+
vm: &VirtualMachine,
1444+
) -> PyResult {
1445+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1446+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1447+
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
1448+
}
1449+
14241450
pub fn init(context: &'static Context) {
14251451
PySet::extend_class(context, context.types.set_type);
1452+
context
1453+
.types
1454+
.set_type
1455+
.slots
1456+
.vectorcall
1457+
.store(Some(vectorcall_set));
1458+
14261459
PyFrozenSet::extend_class(context, context.types.frozenset_type);
1460+
context
1461+
.types
1462+
.frozenset_type
1463+
.slots
1464+
.vectorcall
1465+
.store(Some(vectorcall_frozenset));
1466+
14271467
PySetIterator::extend_class(context, context.types.set_iterator_type);
14281468
}

crates/vm/src/builtins/str.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,8 +1781,25 @@ struct ReplaceArgs {
17811781
count: isize,
17821782
}
17831783

1784+
fn vectorcall_str(
1785+
zelf_obj: &PyObject,
1786+
args: Vec<PyObjectRef>,
1787+
nargs: usize,
1788+
kwnames: Option<&[PyObjectRef]>,
1789+
vm: &VirtualMachine,
1790+
) -> PyResult {
1791+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
1792+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1793+
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
1794+
}
1795+
17841796
pub fn init(ctx: &'static Context) {
17851797
PyStr::extend_class(ctx, ctx.types.str_type);
1798+
ctx.types
1799+
.str_type
1800+
.slots
1801+
.vectorcall
1802+
.store(Some(vectorcall_str));
17861803

17871804
PyStrIterator::extend_class(ctx, ctx.types.str_iterator_type);
17881805
}

crates/vm/src/builtins/tuple.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,9 +695,29 @@ impl IterNext for PyTupleIterator {
695695
}
696696
}
697697

698+
fn vectorcall_tuple(
699+
zelf_obj: &PyObject,
700+
args: Vec<PyObjectRef>,
701+
nargs: usize,
702+
kwnames: Option<&[PyObjectRef]>,
703+
vm: &VirtualMachine,
704+
) -> PyResult {
705+
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
706+
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
707+
// Use the type's own slot_new rather than calling PyTuple::slot_new directly,
708+
// so Rust-level subclasses (e.g. struct sequences) get their custom slot_new called.
709+
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
710+
}
711+
698712
pub(crate) fn init(context: &'static Context) {
699713
PyTuple::extend_class(context, context.types.tuple_type);
700714
PyTupleIterator::extend_class(context, context.types.tuple_iterator_type);
715+
context
716+
.types
717+
.tuple_type
718+
.slots
719+
.vectorcall
720+
.store(Some(vectorcall_tuple));
701721
}
702722

703723
pub(super) fn tuple_hash(elements: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<PyHash> {

crates/vm/src/builtins/type.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2665,6 +2665,13 @@ fn vectorcall_type(
26652665
if nargs == 1 && no_kwargs {
26662666
return Ok(args[0].obj_type());
26672667
}
2668+
} else if zelf.slots.call.load().is_none() {
2669+
// Per-type constructor vectorcall for non-callable types (dict, list, int, etc.)
2670+
// When slots.call is set, slots.vectorcall is for calling instances (e.g. vectorcall_function),
2671+
// not for constructing them, so we must not dispatch it here.
2672+
if let Some(type_vc) = zelf.slots.vectorcall.load() {
2673+
return type_vc(zelf_obj, args, nargs, kwnames, vm);
2674+
}
26682675
}
26692676

26702677
// Fallback: construct FuncArgs and use standard call

0 commit comments

Comments
 (0)