Skip to content

Add per-type vectorcall for builtin constructors#7407

Merged
youknowone merged 1 commit intoRustPython:mainfrom
youknowone:vectorcall
Mar 18, 2026
Merged

Add per-type vectorcall for builtin constructors#7407
youknowone merged 1 commit intoRustPython:mainfrom
youknowone:vectorcall

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Mar 12, 2026

close #7362

Add vectorcall dispatch in vectorcall_type to check each type's own slots.vectorcall before falling back to PyType::call. Clear vectorcall when init or new is overridden in update_slot to ensure subclass correctness.

Implement per-type vectorcall for 12 builtin types:

  • Pattern A (skip args.clone): dict, set, list, bytearray
  • Pattern B (skip dispatch): int, float, str, bool, tuple, frozenset, bytes, complex

Summary by CodeRabbit

  • Bug Fixes
    • Improved error reporting for memory mapping resize operations on Unix and Windows.
    • Fixed environment variable handling on Windows to properly emit terminator sequences when no environment entries exist.
    • Prevented instantiation of sys.getwindowsversion, now raises TypeError.
    • Removed deprecated Windows socket error code from public error mappings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 175c07c7-2428-4690-997c-b0fa19917c4f

📥 Commits

Reviewing files that changed from the base of the PR and between f76e0d1 and ff165a2.

⛔ Files ignored due to path filters (3)
  • Lib/test/test_mmap.py is excluded by !Lib/**
  • Lib/test/test_subprocess.py is excluded by !Lib/**
  • Lib/test/test_sys.py is excluded by !Lib/**
📒 Files selected for processing (4)
  • crates/stdlib/src/mmap.rs
  • crates/vm/src/stdlib/_winapi.rs
  • crates/vm/src/stdlib/errno.rs
  • crates/vm/src/stdlib/sys.rs

📝 Walkthrough

Walkthrough

The PR makes targeted adjustments to error handling, Windows API behavior, and type instantiation control. Changes include reorienting mmap error reporting to use OS errors, handling empty Windows environment mappings with proper NUL termination, removing deprecated WinSock error codes, and preventing instantiation of the Windows version class through a TypeError slot.

Changes

Cohort / File(s) Summary
Error Type Corrections
crates/stdlib/src/mmap.rs
Changed mmap resize failure error reporting from system_error to os_error in Unix and Windows code paths.
Windows Environment Handling
crates/vm/src/stdlib/_winapi.rs
Modified getenvironment to prepend an additional NUL terminator when environment mapping is empty, ensuring \0\0 termination sequence.
Error Code Exports
crates/vm/src/stdlib/errno.rs
Removed WSAHOS constant and deprecated Windows WinSock error code (WSAHOST_NOT_FOUND) from public errno module exports.
Instantiation Prevention
crates/vm/src/stdlib/sys.rs
Added slot_new method to PyWindowsVersion class that raises TypeError to block instantiation attempts.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Error types hop to fresh ground,
Windows paths now safely sound,
Deprecated codes fade away,
Versions block their creation day—
Small fixes, precise and right! 🌟

🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title describes adding per-type vectorcall for builtin constructors, but the actual changes involve error type conversions, Windows environment handling, error code removal, and blocking type instantiation—none of which match the title. Update the PR title to accurately reflect the actual changes, such as 'Fix error handling and type instantiation issues' or provide code changes matching the stated title objective.
Linked Issues check ⚠️ Warning The linked issue #7362 requires implementing per-type vectorcall dispatch for 12 builtin types with modifications to vectorcall_type and slot clearing on init/new override, but the PR shows unrelated changes to error codes, environment variables, and type instantiation blocks instead. Implement the vectorcall dispatch modifications to type.rs and slot.rs, and add vectorcall implementations for the 12 builtin types specified in issue #7362.
Out of Scope Changes check ⚠️ Warning All four file changes (mmap.rs error handling, _winapi.rs environment, errno.rs error codes, sys.rs type blocking) are out of scope relative to the vectorcall feature described in issue #7362. Remove unrelated changes and focus only on implementing per-type vectorcall dispatch for builtin constructors as specified in the linked issue.
Docstring Coverage ⚠️ Warning Docstring coverage is 79.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 12, 2026

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[ ] test: cpython/Lib/test/test_mmap.py (TODO: 1)

dependencies:

dependent tests: (2 tests)

  • mmap: test_os
    • multiprocessing.shared_memory: test_genericalias

[x] lib: cpython/Lib/subprocess.py
[x] test: cpython/Lib/test/test_subprocess.py (TODO: 3)

dependencies:

  • subprocess

dependent tests: (81 tests)

  • subprocess: test_android test_asyncio test_atexit test_audit test_bz2 test_c_locale_coercion test_cmd_line test_cmd_line_script test_ctypes test_dtrace test_faulthandler test_file_eintr test_gc test_gzip test_inspect test_json test_launcher test_msvcrt test_ntpath test_os test_osx_env test_platform test_plistlib test_poll test_py_compile test_quopri test_regrtest test_repl test_runpy test_script_helper test_select test_shutil test_signal test_site test_sqlite3 test_subprocess test_support test_sys test_sysconfig test_tempfile test_threading test_traceback test_unittest test_urllib2 test_utf8_mode test_venv test_wait3 test_webbrowser test_zipfile
    • asyncio: test_asyncio test_contextlib_async test_logging test_sys_settrace test_unittest
    • ctypes.util: test_ctypes
    • ensurepip: test_ensurepip
    • http.server: test_httpservers test_robotparser test_urllib2_localnet test_xmlrpc
      • pydoc: test_enum test_pydoc
      • wsgiref.simple_server: test_wsgiref
      • xmlrpc.server: test_docxmlrpc
    • imaplib: test_imaplib
    • multiprocessing.util: test_compileall test_concurrent_futures
    • platform: test__locale test__osx_support test_baseexception test_builtin test_cmath test_fcntl test_math test_mimetypes test_posix test_socket test_ssl test_time test_winreg
    • uuid:
      • wave: test_wave

[x] test: cpython/Lib/test/test_sys.py (TODO: 7)
[ ] test: cpython/Lib/test/test_syslog.py (TODO: 2)
[ ] test: cpython/Lib/test/test_sys_setprofile.py
[ ] test: cpython/Lib/test/test_sys_settrace.py (TODO: 45)
[ ] test: cpython/Lib/test/test_audit.py
[ ] test: cpython/Lib/test/audit-tests.py

dependencies:

dependent tests: (225 tests)

  • sys: regrtestdata test___all__ test__colorize test__locale test__osx_support test_android test_annotationlib test_argparse test_array test_ast test_asyncio test_audit test_bdb test_bigaddrspace test_bigmem test_bisect test_buffer test_builtin test_bytes test_bz2 test_c_locale_coercion test_calendar test_call test_class test_cmath test_cmd test_cmd_line test_cmd_line_script test_code test_code_module test_codeccallbacks test_codecs test_collections test_compile test_compileall test_complex test_concurrent_futures test_context test_contextlib test_coroutines test_csv test_ctypes test_datetime test_dbm test_dbm_sqlite3 test_decimal test_descr test_dict test_difflib test_dis test_doctest test_doctest2 test_docxmlrpc test_dtrace test_dynamic test_dynamicclassattribute test_email test_ensurepip test_enum test_enumerate test_eof test_except_star test_exceptions test_faulthandler test_fcntl test_file test_file_eintr test_fileinput test_fileio test_float test_fork1 test_format test_fractions test_frame test_frozen test_functools test_future_stmt test_gc test_generators test_genericpath test_getopt test_glob test_grammar test_gzip test_hash test_hashlib test_http_cookiejar test_httpservers test_importlib test_inspect test_int test_io test_iter test_itertools test_json test_largefile test_launcher test_list test_listcomps test_locale test_logging test_long test_lzma test_mailbox test_marshal test_math test_memoryio test_memoryview test_metaclass test_mimetypes test_mmap test_monitoring test_msvcrt test_multiprocessing_forkserver test_multiprocessing_main_handling test_multiprocessing_spawn test_netrc test_ntpath test_numeric_tower test_operator test_optparse test_ordered_dict test_os test_osx_env test_pathlib test_patma test_peepholer test_pickle test_pkg test_pkgutil test_platform test_plistlib test_popen test_posix test_posixpath test_print test_property test_pty test_pwd test_py_compile test_pyclbr test_pydoc test_pyexpat test_quopri test_raise test_range test_re test_regrtest test_repl test_reprlib test_resource test_runpy test_sax test_scope test_script_helper test_select test_selectors test_shutil test_signal test_site test_slice test_smtplib test_socket test_sqlite3 test_ssl test_stable_abi_ctypes test_stat test_statistics test_str test_strftime test_string_literals test_strtod test_struct test_subprocess test_support test_sys test_sys_setprofile test_sys_settrace test_sysconfig test_syslog test_tarfile test_tempfile test_termios test_threadedtempfile test_threading test_threading_local test_threadsignals test_time test_timeit test_tomllib test_tools test_trace test_traceback test_tuple test_type_comments test_types test_typing test_unicode_file test_unicode_file_functions test_unicodedata test_unittest test_univnewlines test_urllib test_urllib2 test_urllib2net test_urlparse test_utf8_mode test_uuid test_venv test_wait3 test_wait4 test_wave test_weakref test_webbrowser test_winconsoleio test_winreg test_with test_wsgiref test_xml_etree test_xmlrpc test_zipapp test_zipfile test_zipfile64 test_zipimport test_zlib

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

@youknowone youknowone force-pushed the vectorcall branch 2 times, most recently from 2b764a2 to a9ee2e3 Compare March 17, 2026 11:38
@youknowone youknowone marked this pull request as ready for review March 17, 2026 11:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
crates/vm/src/builtins/float.rs (1)

528-538: Extract a shared vectorcall-to-slot_new helper to avoid drift.

Line 528-Line 538 duplicates the same trampoline used by multiple builtins. Centralizing this in one helper would keep behavior consistent and lower maintenance risk.

Proposed refactor sketch
+// e.g. in a shared builtins/type helper module
+pub(crate) fn vectorcall_via_slot_new(
+    zelf_obj: &PyObject,
+    args: Vec<PyObjectRef>,
+    nargs: usize,
+    kwnames: Option<&[PyObjectRef]>,
+    vm: &VirtualMachine,
+) -> PyResult {
+    let zelf: &Py<PyType> = 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)
+}

Then each builtin can just store that helper in its slots.vectorcall.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/float.rs` around lines 528 - 538, The vectorcall_float
trampoline duplicates the pattern of converting vectorcall args to FuncArgs and
invoking the type's slot_new (see vectorcall_float,
FuncArgs::from_vectorcall_owned, and
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)); extract that
logic into a single reusable helper (e.g., a function that accepts zelf_obj,
args, nargs, kwnames, vm and performs the downcast, FuncArgs conversion, and
calls slot_new) and have builtins set their slots.vectorcall to point to this
helper to avoid drift and ensure consistent behavior across types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/vm/src/builtins/dict.rs`:
- Around line 1438-1449: The vectorcall_dict fast-path constructs a base dict
with PyDict::default().into_ref_with_type(...) and PyDict::slot_init(...)
without invoking the type's constructor hooks, so it must detect when the
receiver type has custom new/init hooks and fall back to the generic
PyType::call path; update vectorcall_dict to check the target type's slots (e.g.
inspect zelf.slots.new or zelf.slots.init or whether the type is exactly dict)
and if non-default hooks exist (or the type != built-in dict) call
PyType::call(zelf_obj, args, vm) (or the equivalent generic constructor path)
instead of the fast default+slot_init; apply the same pattern to the analogous
fast-paths in list.rs and bytearray.rs to preserve proper subclass constructor
semantics.

In `@crates/vm/src/builtins/tuple.rs`:
- Around line 698-709: The vectorcall_tuple implementation uses the type's
slot_new but returns immediately, skipping any subtype slot_init; update
vectorcall_tuple to mirror PyType::call by invoking the type's slots.new
(zelf.slots.new) to create the instance, then if the type has a non-None
slots.init (zelf.slots.init.load().is_some()) call that init slot with the newly
created object and the original FuncArgs (func_args) and vm before returning the
instance; do the same fix in the bytes vectorcall implementation to ensure
Rust-defined subtypes receive their post-new initializer.

In `@crates/vm/src/builtins/type.rs`:
- Around line 2668-2674: The branch currently dispatches an inherited vectorcall
when zelf.slots.call is None but doesn't check whether slots.new allows
instantiation; update the condition that calls type_vc so it also requires
zelf.slots.new.load().is_some() (i.e., only run type_vc(zelf_obj, args, nargs,
kwnames, vm) when vectorcall is Some AND slots.new is Some) to prevent
dispatching constructors for DISALLOW_INSTANTIATION types; alternatively, when
implementing set_new() for DISALLOW_INSTANTIATION ensure it clears
slots.vectorcall alongside setting slots.new to None so the invariant (no
vectorcall if no new) holds.

In `@crates/vm/src/types/slot.rs`:
- Around line 828-833: The inherit_from_mro handling for SlotAccessor::TpInit
(and TpNew) must also restore the vectorcall slot when the primary slot is
deleted: mirror the TpCall logic by copying the ancestor's vectorcall into
self.slots.vectorcall in the non-ADD (deletion) path so heap types regain
fast-path calls; locate the TpInit/TpNew cases (where update_main_slot! is used
for init/init_wrapper/Init) and add the same vectorcall restoration that TpCall
performs in inherit_from_mro.

---

Nitpick comments:
In `@crates/vm/src/builtins/float.rs`:
- Around line 528-538: The vectorcall_float trampoline duplicates the pattern of
converting vectorcall args to FuncArgs and invoking the type's slot_new (see
vectorcall_float, FuncArgs::from_vectorcall_owned, and
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)); extract that
logic into a single reusable helper (e.g., a function that accepts zelf_obj,
args, nargs, kwnames, vm and performs the downcast, FuncArgs conversion, and
calls slot_new) and have builtins set their slots.vectorcall to point to this
helper to avoid drift and ensure consistent behavior across types.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 81cce507-bbb7-45af-85bc-bfd1b573eedf

📥 Commits

Reviewing files that changed from the base of the PR and between 2ea5960 and a9ee2e3.

📒 Files selected for processing (14)
  • crates/vm/src/builtins/bool.rs
  • crates/vm/src/builtins/bytearray.rs
  • crates/vm/src/builtins/bytes.rs
  • crates/vm/src/builtins/complex.rs
  • crates/vm/src/builtins/dict.rs
  • crates/vm/src/builtins/float.rs
  • crates/vm/src/builtins/int.rs
  • crates/vm/src/builtins/list.rs
  • crates/vm/src/builtins/set.rs
  • crates/vm/src/builtins/str.rs
  • crates/vm/src/builtins/tuple.rs
  • crates/vm/src/builtins/type.rs
  • crates/vm/src/stdlib/os.rs
  • crates/vm/src/types/slot.rs

Comment on lines +1438 to +1449
fn vectorcall_dict(
zelf_obj: &PyObject,
args: Vec<PyObjectRef>,
nargs: usize,
kwnames: Option<&[PyObjectRef]>,
vm: &VirtualMachine,
) -> PyResult {
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
let obj = PyDict::default().into_ref_with_type(vm, zelf.to_owned())?;
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
PyDict::slot_init(obj.clone().into(), func_args, vm)?;
Ok(obj.into())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This fast path hardwires base dict construction.

PyDict::default().into_ref_with_type(...) plus PyDict::slot_init(...) never consults zelf.slots.new / zelf.slots.init, so a Rust-defined dict subtype can inherit this vectorcall but skip its own constructor hooks. That diverges from PyType::call. The same default()+slot_init() pattern in list.rs and bytearray.rs needs the same guard.

🛠️ Conservative fallback
 fn vectorcall_dict(
     zelf_obj: &PyObject,
     args: Vec<PyObjectRef>,
     nargs: usize,
     kwnames: Option<&[PyObjectRef]>,
     vm: &VirtualMachine,
 ) -> PyResult {
     let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
+    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
+    if !zelf.is(vm.ctx.types.dict_type) {
+        return PyType::call(zelf, func_args, vm);
+    }
     let obj = PyDict::default().into_ref_with_type(vm, zelf.to_owned())?;
-    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
     PyDict::slot_init(obj.clone().into(), func_args, vm)?;
     Ok(obj.into())
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/dict.rs` around lines 1438 - 1449, The vectorcall_dict
fast-path constructs a base dict with PyDict::default().into_ref_with_type(...)
and PyDict::slot_init(...) without invoking the type's constructor hooks, so it
must detect when the receiver type has custom new/init hooks and fall back to
the generic PyType::call path; update vectorcall_dict to check the target type's
slots (e.g. inspect zelf.slots.new or zelf.slots.init or whether the type is
exactly dict) and if non-default hooks exist (or the type != built-in dict) call
PyType::call(zelf_obj, args, vm) (or the equivalent generic constructor path)
instead of the fast default+slot_init; apply the same pattern to the analogous
fast-paths in list.rs and bytearray.rs to preserve proper subclass constructor
semantics.

Comment on lines +698 to +709
fn vectorcall_tuple(
zelf_obj: &PyObject,
args: Vec<PyObjectRef>,
nargs: usize,
kwnames: Option<&[PyObjectRef]>,
vm: &VirtualMachine,
) -> PyResult {
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This still skips subtype slot_init.

Using zelf.slots.new fixes the custom-slot_new case, but the direct return bypasses the post-slot_new init step that PyType::call would run. A Rust-defined tuple subtype with a non-None slots.init will inherit this vectorcall and never see its initializer. The same issue exists in crates/vm/src/builtins/bytes.rs on Lines 89-99.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/tuple.rs` around lines 698 - 709, The vectorcall_tuple
implementation uses the type's slot_new but returns immediately, skipping any
subtype slot_init; update vectorcall_tuple to mirror PyType::call by invoking
the type's slots.new (zelf.slots.new) to create the instance, then if the type
has a non-None slots.init (zelf.slots.init.load().is_some()) call that init slot
with the newly created object and the original FuncArgs (func_args) and vm
before returning the instance; do the same fix in the bytes vectorcall
implementation to ensure Rust-defined subtypes receive their post-new
initializer.

Comment on lines +828 to +833
SlotAccessor::TpInit => {
update_main_slot!(init, init_wrapper, Init);
if ADD {
self.slots.vectorcall.store(None);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify where vectorcall is cleared/restored in slot update flows.
rg -n -C3 'SlotAccessor::TpInit|SlotAccessor::TpNew|slots\.vectorcall\.store' crates/vm/src/types/slot.rs

# Check whether any non-init bootstrapping path recomputes vectorcall for heap types.
rg -n -C3 'vectorcall\.store\(Some|vectorcall\.store\(None|update_slot<false>' crates/vm/src/types

Repository: RustPython/RustPython

Length of output: 2674


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the full TpInit and TpNew case implementations
sed -n '828,841p' crates/vm/src/types/slot.rs

# Look for inherit_from_mro implementation and usage
rg -n 'fn inherit_from_mro|inherit_from_mro' crates/vm/src/types/slot.rs | head -20

# Check if there are other slots that handle vectorcall restoration
rg -n 'vectorcall.store' crates/vm/src/types/slot.rs

Repository: RustPython/RustPython

Length of output: 2019


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the SlotAccessor enum and understand all variants
rg -n 'enum SlotAccessor' crates/vm/src/types/slot.rs -A 50

# Check how other slot updates handle the ADD/non-ADD distinction
rg -n 'if ADD' crates/vm/src/types/slot.rs | head -30

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for where TpInit handling differs from TpNew
# Check the broader context around TpInit 
sed -n '800,900p' crates/vm/src/types/slot.rs

Repository: RustPython/RustPython

Length of output: 4610


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the update_main_slot macro definition
rg -n 'macro_rules! update_main_slot' crates/vm/src/types/slot.rs -A 20

# Find inherit_from_mro implementation
rg -n 'fn inherit_from_mro' crates/vm/src/types/slot.rs -A 30

Repository: RustPython/RustPython

Length of output: 1056


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if TpCall also has the same issue
sed -n '820,826p' crates/vm/src/types/slot.rs

# Look at the context around TpInit to understand the slot update pattern
sed -n '700,750p' crates/vm/src/types/slot.rs

Repository: RustPython/RustPython

Length of output: 2880


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for how non-ADD is handled elsewhere in the function
# to see if there's a pattern for handling deletion
rg -n 'if ADD \{' crates/vm/src/types/slot.rs -B 2 -A 8 | head -100

Repository: RustPython/RustPython

Length of output: 5021


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find inherit_from_mro implementation
rg -n 'fn inherit_from_mro' crates/vm/src/types/slot.rs -A 50 | head -80

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if vectorcall is recomputed anywhere during slot updates
rg -n 'vectorcall' crates/vm/src/types/slot.rs -B 2 -A 2

# Look for any comments about vectorcall behavior
rg -n -C5 'vectorcall' crates/vm/src/types/slot.rs | grep -A 5 -B 5 'inherit\|restore\|compute'

Repository: RustPython/RustPython

Length of output: 2034


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if there's a method that recomputes vectorcall for heap types
rg -n 'recompute.*vectorcall|update.*vectorcall' crates/vm/src/types/

# Check the SlotAccessor type to understand its structure
sed -n '650,702p' crates/vm/src/types/slot.rs

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search more broadly for inherit_from_mro
rg -n 'inherit_from_mro' crates/vm/src/types/slot.rs | head -5

# Get the implementation by searching in the same file
grep -n 'fn inherit_from_mro' crates/vm/src/types/slot.rs

Repository: RustPython/RustPython

Length of output: 381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search in the entire types directory
rg -n 'fn inherit_from_mro' crates/vm/src/types/ -A 30

Repository: RustPython/RustPython

Length of output: 2334


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look at how vectorcall is specifically managed
# Check if there's any place where it's computed or cleared on inheritance
rg -n 'vectorcall' crates/vm/src/ --type rust | grep -i 'inherit\|mro\|parent'

Repository: RustPython/RustPython

Length of output: 144


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get the full inherit_from_mro implementation to see all slots it handles
sed -n '414,550p' crates/vm/src/types/slot_defs.rs

Repository: RustPython/RustPython

Length of output: 6065


🏁 Script executed:

#!/bin/bash
set -euo pipefixpipfail

# Verify the exact behavior: when TpInit/TpCall/TpNew delete happens (non-ADD),
# does the macro actually expand to inherit_from_mro?
# Let's trace through what happens for TpInit on non-ADD
sed -n '702,728p' crates/vm/src/types/slot.rs

Repository: RustPython/RustPython

Length of output: 124


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if vectorcall is properly initialized/cleared behavior for __init__ and __new__
# Look for any comments explaining the vectorcall clearing logic
rg -n -B5 -A5 'vectorcall.*None' crates/vm/src/types/slot.rs

Repository: RustPython/RustPython

Length of output: 1330


Restore vectorcall inheritance on __init__/__new__ deletion.

When deleting __init__ or __new__ (non-ADD path), inherit_from_mro restores the slot from the MRO but does not restore vectorcall. This differs from TpCall, which properly inherits both. Without restoring vectorcall on deletion, heap types fall back to the slow path even when the inherited slot supports fast-path calls, losing performance optimization that was available in the parent class.

The fix should mirror TpCall's behavior in inherit_from_mro (slot_defs.rs line 456): include vectorcall restoration for TpInit and TpNew cases, not just the primary slots.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/types/slot.rs` around lines 828 - 833, The inherit_from_mro
handling for SlotAccessor::TpInit (and TpNew) must also restore the vectorcall
slot when the primary slot is deleted: mirror the TpCall logic by copying the
ancestor's vectorcall into self.slots.vectorcall in the non-ADD (deletion) path
so heap types regain fast-path calls; locate the TpInit/TpNew cases (where
update_main_slot! is used for init/init_wrapper/Init) and add the same
vectorcall restoration that TpCall performs in inherit_from_mro.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
crates/vm/src/builtins/tuple.rs (1)

698-709: ⚠️ Potential issue | 🟠 Major

vectorcall_tuple skips subtype slot_init after slot_new.

This path returns right after slot_new, so subtype init hooks are bypassed. It should mirror PyType::call’s newinit sequence when applicable.

💡 Suggested fix
 fn vectorcall_tuple(
     zelf_obj: &PyObject,
     args: Vec<PyObjectRef>,
     nargs: usize,
     kwnames: Option<&[PyObjectRef]>,
     vm: &VirtualMachine,
 ) -> PyResult {
     let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
     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)
+    let obj = (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args.clone(), vm)?;
+    if obj.class().fast_issubclass(zelf) {
+        if let Some(init_method) = obj.class().slots.init.load() {
+            init_method(obj.clone(), func_args, vm)?;
+        }
+    }
+    Ok(obj)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/tuple.rs` around lines 698 - 709, vectorcall_tuple
currently calls the type's slot_new and returns immediately, skipping any
subclass slot_init; update vectorcall_tuple to mirror PyType::call's new→init
sequence: after invoking (zelf.slots.new.load().unwrap()) and obtaining the
created object, check whether the type has a non-default slot_init (e.g., via
zelf.slots.init.load() or the same mechanism used in PyType::call), and if so
call that init slot with the same FuncArgs and vm; propagate any error from
slot_init and return the created instance on success, ensuring
ownership/borrowing follows the existing pattern (use zelf.to_owned() and the
created PyObjectRef as in the surrounding code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/vm/src/stdlib/os.rs`:
- Around line 1378-1379: The code that populates the
st_atime_ns/st_mtime_ns/st_ctime_ns fields is assigning raw second values
(items[i], etc.) directly (see vm.is_none and items usage), but the *_ns
contract expects nanoseconds; change the assignment so when the corresponding
*_ns slot (e.g., items[i+?] where st_*_ns are filled) is missing you assign the
seconds value multiplied by 1_000_000_000 (i.e., seconds * 1_000_000_000) rather
than the raw seconds clone; preserve the numeric type used elsewhere and ensure
you perform the multiplication before assigning to items[...] for st_atime_ns,
st_mtime_ns and st_ctime_ns.

---

Duplicate comments:
In `@crates/vm/src/builtins/tuple.rs`:
- Around line 698-709: vectorcall_tuple currently calls the type's slot_new and
returns immediately, skipping any subclass slot_init; update vectorcall_tuple to
mirror PyType::call's new→init sequence: after invoking
(zelf.slots.new.load().unwrap()) and obtaining the created object, check whether
the type has a non-default slot_init (e.g., via zelf.slots.init.load() or the
same mechanism used in PyType::call), and if so call that init slot with the
same FuncArgs and vm; propagate any error from slot_init and return the created
instance on success, ensuring ownership/borrowing follows the existing pattern
(use zelf.to_owned() and the created PyObjectRef as in the surrounding code).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: ef159242-c3e7-44ca-9737-7266cebe3ec4

📥 Commits

Reviewing files that changed from the base of the PR and between a9ee2e3 and 3c666d6.

📒 Files selected for processing (13)
  • crates/vm/src/builtins/bool.rs
  • crates/vm/src/builtins/bytearray.rs
  • crates/vm/src/builtins/dict.rs
  • crates/vm/src/builtins/float.rs
  • crates/vm/src/builtins/int.rs
  • crates/vm/src/builtins/list.rs
  • crates/vm/src/builtins/set.rs
  • crates/vm/src/builtins/str.rs
  • crates/vm/src/builtins/tuple.rs
  • crates/vm/src/builtins/type.rs
  • crates/vm/src/stdlib/os.rs
  • crates/vm/src/types/slot.rs
  • crates/vm/src/types/slot_defs.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/vm/src/builtins/list.rs
  • crates/vm/src/builtins/int.rs
  • crates/vm/src/builtins/bytearray.rs
  • crates/vm/src/builtins/bool.rs
  • crates/vm/src/builtins/set.rs
  • crates/vm/src/builtins/type.rs

Comment on lines +1378 to +1379
if vm.is_none(&items[i + 6]) {
items[i + 6] = items[i].clone();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

*_ns fields are populated in seconds, not nanoseconds.

On Line 1379, missing st_atime_ns/st_mtime_ns/st_ctime_ns are set to the raw integer-second values. That breaks the _ns contract (1 second should become 1_000_000_000 ns).

💡 Suggested fix
+            let billion = vm.ctx.new_int(1_000_000_000).into();
             for i in 7..=9 {
                 if vm.is_none(&items[i + 3]) {
                     items[i + 3] = items[i].clone();
                 }
                 if vm.is_none(&items[i + 6]) {
-                    items[i + 6] = items[i].clone();
+                    items[i + 6] = vm._mul(&items[i], &billion)?;
                 }
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if vm.is_none(&items[i + 6]) {
items[i + 6] = items[i].clone();
let billion = vm.ctx.new_int(1_000_000_000).into();
for i in 7..=9 {
if vm.is_none(&items[i + 3]) {
items[i + 3] = items[i].clone();
}
if vm.is_none(&items[i + 6]) {
items[i + 6] = vm._mul(&items[i], &billion)?;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/stdlib/os.rs` around lines 1378 - 1379, The code that populates
the st_atime_ns/st_mtime_ns/st_ctime_ns fields is assigning raw second values
(items[i], etc.) directly (see vm.is_none and items usage), but the *_ns
contract expects nanoseconds; change the assignment so when the corresponding
*_ns slot (e.g., items[i+?] where st_*_ns are filled) is missing you assign the
seconds value multiplied by 1_000_000_000 (i.e., seconds * 1_000_000_000) rather
than the raw seconds clone; preserve the numeric type used elsewhere and ensure
you perform the multiplication before assigning to items[...] for st_atime_ns,
st_mtime_ns and st_ctime_ns.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
crates/vm/src/stdlib/os.rs (1)

1371-1379: ⚠️ Potential issue | 🟠 Major

st_*_ns fallback handling is missing.

Line 1374 leaves nanosecond fields unset when omitted. That can break st_atime_ns/st_mtime_ns/st_ctime_ns consistency for sequence-based construction.

💡 Suggested fix
             for i in 7..=9 {
                 if vm.is_none(&items[i + 3]) {
                     items[i + 3] = items[i].clone();
                 }
+                if vm.is_none(&items[i + 6]) {
+                    let billion: PyObjectRef = vm.ctx.new_int(1_000_000_000).into();
+                    items[i + 6] = vm._mul(&items[i], &billion)?;
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/stdlib/os.rs` around lines 1371 - 1379, The loop that copies
integer time fields to float timestamp slots (for i in 7..=9) neglects to set
the corresponding nanosecond slots (items[i+6]) when they are omitted; update
the loop in the same block to check vm.is_none(&items[i + 6]) and, if missing,
set items[i + 6] to a zero-nanoseconds value (e.g., an integer zero) so
st_atime_ns/st_mtime_ns/st_ctime_ns are populated consistently alongside the
float timestamps and integer seconds.
crates/vm/src/builtins/list.rs (1)

771-782: ⚠️ Potential issue | 🟠 Major

This fast path also skips subtype slot_new.

PyList::default().into_ref_with_type(...) has the same hazard as dict: a Rust-defined list subtype can inherit this vectorcall and be allocated as a plain PyList instead of going through its own constructor path. Please guard this fast path on the builtin list slots or fall back to the generic type call path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/list.rs` around lines 771 - 782, The fast path in
vectorcall_list incorrectly constructs a PyList for arbitrary subtypes via
PyList::default().into_ref_with_type(...), skipping subtype construction; change
vectorcall_list to only take the fast path when the target type is the exact
builtin list type or when its new/slot_new is the builtin implementation (i.e.,
check the type against vm.ctx.types.list_type or compare its slot_new to the
builtin slot), and otherwise fall back to the generic type call path so subclass
constructors (slot_new) run; keep references to vectorcall_list,
PyList::default().into_ref_with_type, PyList::slot_init and slot_new to locate
and update the logic.
crates/vm/src/builtins/dict.rs (1)

1438-1449: ⚠️ Potential issue | 🟠 Major

This still bypasses subtype slot_new.

PyDict::default().into_ref_with_type(...) never consults the target type’s own constructor slots, so a Rust-defined dict subtype can inherit this vectorcall and skip its allocator/init hooks here. Please fall back to the generic type call path unless the target is still using dict’s builtin new/init slots.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/builtins/dict.rs` around lines 1438 - 1449, vectorcall_dict
currently creates an instance with PyDict::default().into_ref_with_type(...)
which bypasses the target type’s own constructor slots; change it to first
detect whether the target type still uses dict’s builtin new/init slots (i.e.,
compare the target type’s slot_new/slot_init to PyDict’s defaults or check type
identity), and only use PyDict::default().into_ref_with_type + PyDict::slot_init
when they match; otherwise fall back to the generic type call path (invoke the
type’s normal call/constructor path via the VM so overridden alloc/init run)
instead of instantiating directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/vm/src/types/slot_defs.rs`:
- Around line 456-469: The current inheritance code gates copying a parent
type's constructor fast-path behind the presence of an instance call slot (it
checks cls.slots.call before taking cls.slots.vectorcall), which prevents
subclasses of builtins (e.g., float/list) from inheriting constructor
vectorcall; change the find_map closure used to compute inherited_vc to
unconditionally return cls.slots.vectorcall.load() (i.e., remove the if
cls.slots.call.load().is_some() branch) so
typ.slots.vectorcall.store(inherited_vc) can copy constructor vectorcall
regardless of the parent's slots.call presence; apply the same removal of the
call-slot gating in the corresponding copy/inheritance site that sets vectorcall
(the symmetric code that assigns typ.slots.vectorcall).

---

Duplicate comments:
In `@crates/vm/src/builtins/dict.rs`:
- Around line 1438-1449: vectorcall_dict currently creates an instance with
PyDict::default().into_ref_with_type(...) which bypasses the target type’s own
constructor slots; change it to first detect whether the target type still uses
dict’s builtin new/init slots (i.e., compare the target type’s
slot_new/slot_init to PyDict’s defaults or check type identity), and only use
PyDict::default().into_ref_with_type + PyDict::slot_init when they match;
otherwise fall back to the generic type call path (invoke the type’s normal
call/constructor path via the VM so overridden alloc/init run) instead of
instantiating directly.

In `@crates/vm/src/builtins/list.rs`:
- Around line 771-782: The fast path in vectorcall_list incorrectly constructs a
PyList for arbitrary subtypes via PyList::default().into_ref_with_type(...),
skipping subtype construction; change vectorcall_list to only take the fast path
when the target type is the exact builtin list type or when its new/slot_new is
the builtin implementation (i.e., check the type against vm.ctx.types.list_type
or compare its slot_new to the builtin slot), and otherwise fall back to the
generic type call path so subclass constructors (slot_new) run; keep references
to vectorcall_list, PyList::default().into_ref_with_type, PyList::slot_init and
slot_new to locate and update the logic.

In `@crates/vm/src/stdlib/os.rs`:
- Around line 1371-1379: The loop that copies integer time fields to float
timestamp slots (for i in 7..=9) neglects to set the corresponding nanosecond
slots (items[i+6]) when they are omitted; update the loop in the same block to
check vm.is_none(&items[i + 6]) and, if missing, set items[i + 6] to a
zero-nanoseconds value (e.g., an integer zero) so
st_atime_ns/st_mtime_ns/st_ctime_ns are populated consistently alongside the
float timestamps and integer seconds.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 1c7adec1-9b97-4816-a120-0a8b7fb9c5d7

📥 Commits

Reviewing files that changed from the base of the PR and between 3c666d6 and 2ef3473.

📒 Files selected for processing (12)
  • crates/vm/src/builtins/bool.rs
  • crates/vm/src/builtins/dict.rs
  • crates/vm/src/builtins/float.rs
  • crates/vm/src/builtins/int.rs
  • crates/vm/src/builtins/list.rs
  • crates/vm/src/builtins/set.rs
  • crates/vm/src/builtins/str.rs
  • crates/vm/src/builtins/tuple.rs
  • crates/vm/src/builtins/type.rs
  • crates/vm/src/stdlib/os.rs
  • crates/vm/src/types/slot.rs
  • crates/vm/src/types/slot_defs.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/vm/src/builtins/int.rs
  • crates/vm/src/builtins/type.rs
  • crates/vm/src/builtins/tuple.rs
  • crates/vm/src/types/slot.rs

Comment on lines +1424 to +1435
fn vectorcall_set(
zelf_obj: &PyObject,
args: Vec<PyObjectRef>,
nargs: usize,
kwnames: Option<&[PyObjectRef]>,
vm: &VirtualMachine,
) -> PyResult {
let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
let obj = PySet::default().into_ref_with_type(vm, zelf.to_owned())?;
let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
PySet::slot_init(obj.clone().into(), func_args, vm)?;
Ok(obj.into())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

vectorcall_set bypasses subtype construction.

Unlike vectorcall_frozenset just below, this always allocates PySet directly and never consults zelf.slots.new. A Rust-defined set subtype can therefore inherit the vectorcall slot but skip its own constructor path. Please guard this fast path on the builtin set slots or fall back to the generic type call path.

Conservative fallback
 use crate::{
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
     atomic_func,
     class::PyClassImpl,
     common::{ascii, hash::PyHash, lock::PyMutex, rc::PyRc, wtf8::Wtf8Buf},
     convert::ToPyResult,
     dict_inner::{self, DictSize},
     function::{ArgIterable, FuncArgs, OptionalArg, PosArgs, PyArithmeticValue, PyComparisonValue},
     protocol::{PyIterReturn, PyNumberMethods, PySequenceMethods},
     recursion::ReprGuard,
     types::AsNumber,
     types::{
-        AsSequence, Comparable, Constructor, DefaultConstructor, Hashable, Initializer, IterNext,
-        Iterable, PyComparisonOp, Representable, SelfIter,
+        AsSequence, Callable, Comparable, Constructor, DefaultConstructor, Hashable, Initializer,
+        IterNext, Iterable, PyComparisonOp, Representable, SelfIter,
     },
     utils::collection_repr,
     vm::VirtualMachine,
 };
@@
 fn vectorcall_set(
     zelf_obj: &PyObject,
     args: Vec<PyObjectRef>,
     nargs: usize,
     kwnames: Option<&[PyObjectRef]>,
     vm: &VirtualMachine,
 ) -> PyResult {
     let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
+    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
+    if !zelf.is(vm.ctx.types.set_type) {
+        return PyType::call(zelf, func_args, vm);
+    }
     let obj = PySet::default().into_ref_with_type(vm, zelf.to_owned())?;
-    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
     PySet::slot_init(obj.clone().into(), func_args, vm)?;
     Ok(obj.into())
 }

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.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
crates/vm/src/types/slot_defs.rs (1)

462-468: ⚠️ Potential issue | 🟠 Major

Constructor vectorcall inheritance is blocked for non-callable builtins.

Line 462 and Line 590 gate slots.vectorcall inheritance on slots.call. That prevents subclasses of constructor-only builtins (dict, list, int, etc.) from inheriting constructor vectorcall and forces fallback to generic construction (crates/vm/src/builtins/type.rs, Line 2686).

Suggested fix
             Self::TpCall => {
                 inherit_main!(call);
-                let inherited_vc = mro.iter().find_map(|cls| {
-                    if cls.slots.call.load().is_some() {
-                        cls.slots.vectorcall.load()
-                    } else {
-                        None
-                    }
-                });
+                let inherited_vc = mro.iter().find_map(|cls| cls.slots.vectorcall.load());
                 typ.slots.vectorcall.store(inherited_vc);
             }
             Self::TpCall => {
                 copy_main!(call);
-                if typ.slots.vectorcall.load().is_none()
-                    && base.slots.call.load().is_some()
-                    && let Some(base_val) = base.slots.vectorcall.load()
+                if typ.slots.vectorcall.load().is_none()
+                    && let Some(base_val) = base.slots.vectorcall.load()
                 {
                     typ.slots.vectorcall.store(Some(base_val));
                 }
             }

Also applies to: 590-594

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/types/slot_defs.rs` around lines 462 - 468, The code currently
prevents inheriting slots.vectorcall when slots.call is absent (see the
inherited_vc computation using mro.iter().find_map), which blocks
constructor-only builtins from inheriting vectorcall; change the predicate to
check only for the presence of cls.slots.vectorcall (not cls.slots.call) so
vectorcall is inherited independently of call. Apply the same fix to the other
similar block that conditions vectorcall on call (the second mro.iter().find_map
instance around slots.vectorcall) so subclasses of constructor-only builtins can
inherit constructor vectorcall.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/vm/src/types/slot_defs.rs`:
- Around line 462-468: The code currently prevents inheriting slots.vectorcall
when slots.call is absent (see the inherited_vc computation using
mro.iter().find_map), which blocks constructor-only builtins from inheriting
vectorcall; change the predicate to check only for the presence of
cls.slots.vectorcall (not cls.slots.call) so vectorcall is inherited
independently of call. Apply the same fix to the other similar block that
conditions vectorcall on call (the second mro.iter().find_map instance around
slots.vectorcall) so subclasses of constructor-only builtins can inherit
constructor vectorcall.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 348b0c7b-19d8-453b-91ae-a97e06e7c799

📥 Commits

Reviewing files that changed from the base of the PR and between 2ef3473 and f76e0d1.

📒 Files selected for processing (12)
  • crates/vm/src/builtins/bool.rs
  • crates/vm/src/builtins/dict.rs
  • crates/vm/src/builtins/float.rs
  • crates/vm/src/builtins/int.rs
  • crates/vm/src/builtins/list.rs
  • crates/vm/src/builtins/set.rs
  • crates/vm/src/builtins/str.rs
  • crates/vm/src/builtins/tuple.rs
  • crates/vm/src/builtins/type.rs
  • crates/vm/src/stdlib/os.rs
  • crates/vm/src/types/slot.rs
  • crates/vm/src/types/slot_defs.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/vm/src/builtins/bool.rs
  • crates/vm/src/builtins/list.rs
  • crates/vm/src/builtins/str.rs
  • crates/vm/src/types/slot.rs
  • crates/vm/src/builtins/tuple.rs

@youknowone youknowone force-pushed the vectorcall branch 2 times, most recently from ff165a2 to f76e0d1 Compare March 17, 2026 14:16
@youknowone youknowone merged commit 62edc47 into RustPython:main Mar 18, 2026
26 of 29 checks passed
@youknowone youknowone deleted the vectorcall branch March 18, 2026 12:44
youknowone added a commit to youknowone/RustPython that referenced this pull request Mar 19, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vectercall per type

1 participant