Skip to content

Add per-size tuple freelist (20 buckets × 2000 each)#7361

Merged
youknowone merged 4 commits intoRustPython:mainfrom
youknowone:freelist-more-types
Mar 9, 2026
Merged

Add per-size tuple freelist (20 buckets × 2000 each)#7361
youknowone merged 4 commits intoRustPython:mainfrom
youknowone:freelist-more-types

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Mar 5, 2026

Summary

  • Add TupleFreeList with 20 per-size buckets matching CPython's tuples[PyTuple_MAXSAVESIZE] (sizes 1..=20, 2000 capacity each)
  • freelist_push reads element count from the object and stores in buckets[len-1]
  • freelist_pop takes &Self payload reference to select the correct size bucket
  • Add pyinner_layout<T>() helper for custom freelist Drop impls
  • Update freelist_pop trait signature to accept &Self across all freelist types

CPython alignment

Aspect CPython RustPython
PyTuple_MAXSAVESIZE 20 TUPLE_MAXSAVESIZE = 20
Py_tuple_MAXFREELIST 2000 TUPLE_MAXFREELIST = 2000
Bucket structure tuples[20] array buckets: [Vec; 20]
Push-time type check Py_IS_TYPE(op, &PyTuple_Type) vtable dispatch (HAS_FREELIST=false for structseq) + heaptype_ext.is_none()
Index calculation Py_SIZE(op) - 1 elements.len() - 1
Empty tuple skip Implicit (singleton return) Explicit len == 0 check

Test plan

  • cargo build --release
  • cargo clippy --release
  • test_tuple — pass
  • test_structseq — pass (structseq types correctly excluded from freelist)
  • test_os, test_time, test_stat — pass (structseq-heavy tests)
  • test_sys — pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Performance

    • Improved tuple memory management with per-size caching to reduce allocations for common small/medium tuples.
  • Refactor

    • Unified and modernized internal object reuse across several built-in types for more consistent memory reuse and safer reuse checks; no changes to external behavior.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 5, 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
📝 Walkthrough

Walkthrough

This PR revises the freelist API: adds freelist_hint, changes PyPayload::freelist_push to accept a hint and PyPayload::freelist_pop to accept a payload reference; updates call sites and core helpers; and implements a per-size, thread-local freelist for small PyTuple instances.

Changes

Cohort / File(s) Summary
Trait & Core Infrastructure
crates/vm/src/object/payload.rs, crates/vm/src/object/core.rs
Added freelist_hint(_obj) -> usize; changed freelist_push(obj, _hint: usize) and freelist_pop(_payload: &Self) signatures; added pyinner_layout<T: PyPayload>() -> Layout; updated call sites (e.g., PyRef::new_ref) to pass hint and payload reference and to handle type-mismatch on reuse.
Per-size Tuple Freelist
crates/vm/src/builtins/tuple.rs
Added TupleFreeList with per-length buckets (1..=20), TLS TUPLE_FREELIST, constants MAX_FREELIST/HAS_FREELIST, and implemented freelist_hint, freelist_push(obj, hint), and freelist_pop(&payload) to cache small tuples.
Builtin Types — Signature Updates
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/range.rs, crates/vm/src/builtins/slice.rs
Updated impl PyPayload signatures to freelist_push(obj, _hint: usize) and freelist_pop(_payload: &Self) across multiple builtins; implementations preserved but parameters added (currently unused in many).

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Caller
    participant PyRef as PyRef::new_ref
    participant Payload as T (payload)
    participant Freelist as Thread-local Freelist
    participant Alloc as Allocator

    Caller->>PyRef: request new PyRef<T>
    PyRef->>Payload: construct payload (type info)
    PyRef->>Freelist: call T::freelist_pop(&payload)
    alt freelist hit
        Freelist-->>PyRef: return cached PyObject ptr
        PyRef->>PyRef: initialize reused inner (drop caller typ if needed)
    else freelist miss
        PyRef->>Alloc: allocate PyInner<T> (pyinner_layout)
        Alloc-->>PyRef: return new PyObject
    end
    PyRef-->>Caller: return initialized PyRef<T>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I tunneled through code and tucked tuples tight,
I counted little lengths and hid them out of sight,
With hints I whisper, with payloads I know,
I stash and pop so allocations go slow—
🥕 A hop, a cache, a rabbit’s delight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly matches the main change: it adds a per-size tuple freelist with 20 buckets of 2000 entries each, which is the primary feature introduced in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

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

🤖 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/tuple.rs`:
- Around line 113-118: The freelist_push implementation in unsafe fn
freelist_push currently reads the tuple size from py_tuple.elements.len(), but
default_dealloc calls tp_clear (which calls PyTuple::clear) before push so
elements are already cleared and len becomes 0; change the code so the tuple
length is captured before clearing (or modify the call site to pass the
pre-clear size into freelist_push), then use that captured size for the bucket
selection and the TUPLE_MAXSAVESIZE check; ensure you still skip caching when
the original size was 0 or >TUPLE_MAXSAVESIZE and keep the rest of freelist_push
logic unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 65df5403-da34-438f-9eec-53a4d53aae14

📥 Commits

Reviewing files that changed from the base of the PR and between 3b91466 and ea854ba.

📒 Files selected for processing (10)
  • 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/range.rs
  • crates/vm/src/builtins/slice.rs
  • crates/vm/src/builtins/tuple.rs
  • crates/vm/src/object/core.rs
  • crates/vm/src/object/payload.rs

@youknowone youknowone force-pushed the freelist-more-types branch from ea854ba to 33f54e0 Compare March 5, 2026 16:21
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/builtins/tuple.rs (1)

110-130: ⚠️ Potential issue | 🔴 Critical

Per-size tuple freelist is effectively bypassed at push time.

At Line 112, bucket selection uses py_tuple.elements.len(), but default_dealloc (in core.rs:191-194) calls tp_clear before freelist_push. The PyTuple::clear implementation (lines 52-55) does core::mem::take(&mut self.elements), which empties the elements. When freelist_push runs, len == 0, so lines 113-114 return false and tuples are never cached in size buckets.

To fix this, the tuple length must be captured before tp_clear runs. One approach is to store the original size in the PyTuple payload (e.g., an original_len: usize field set at construction) or pass the pre-clear size into freelist_push via a different mechanism.

,

🤖 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 110 - 130, freelist_push
currently reads the tuple length from py_tuple.elements, but PyTuple::clear
(called by default_dealloc) empties elements before freelist_push runs so len is
always 0; fix by capturing the tuple size before clearing and passing it to
freelist_push (or storing it in the PyTuple payload set at construction) so
bucket selection uses the pre-clear size. Update freelist_push signature (or add
original_len field) and all call sites (notably default_dealloc / tp_clear path)
to supply the original length, and keep the existing bounds check against
TupleFreeList::MAXSAVESIZE and TUPLE_FREELIST bucket logic.
🤖 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/builtins/tuple.rs`:
- Around line 110-130: freelist_push currently reads the tuple length from
py_tuple.elements, but PyTuple::clear (called by default_dealloc) empties
elements before freelist_push runs so len is always 0; fix by capturing the
tuple size before clearing and passing it to freelist_push (or storing it in the
PyTuple payload set at construction) so bucket selection uses the pre-clear
size. Update freelist_push signature (or add original_len field) and all call
sites (notably default_dealloc / tp_clear path) to supply the original length,
and keep the existing bounds check against TupleFreeList::MAXSAVESIZE and
TUPLE_FREELIST bucket logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 1463f3a8-1b7c-461f-9c0c-57ad8b07ed56

📥 Commits

Reviewing files that changed from the base of the PR and between ea854ba and 33f54e0.

📒 Files selected for processing (10)
  • 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/range.rs
  • crates/vm/src/builtins/slice.rs
  • crates/vm/src/builtins/tuple.rs
  • crates/vm/src/object/core.rs
  • crates/vm/src/object/payload.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/vm/src/builtins/list.rs
  • crates/vm/src/builtins/range.rs
  • crates/vm/src/builtins/dict.rs

@youknowone youknowone force-pushed the freelist-more-types branch 3 times, most recently from 6be1e32 to 8a56dd5 Compare March 6, 2026 00:30
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

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

62-63: Store NonNull<PyObject> in the buckets.

That makes the non-null invariant explicit and removes the unchecked conversion on Line 148.

♻️ Suggested cleanup
 struct TupleFreeList {
-    buckets: [Vec<*mut PyObject>; Self::MAXSAVESIZE],
+    buckets: [Vec<NonNull<PyObject>>; Self::MAXSAVESIZE],
 }
...
                 let bucket = &mut list.buckets[len - 1];
                 let stored = if bucket.len() < Self::MAX_FREELIST {
-                    bucket.push(obj);
+                    bucket.push(NonNull::new_unchecked(obj));
                     true
                 } else {
                     false
                 };
...
-                let result = list.buckets[len - 1]
-                    .pop()
-                    .map(|p| unsafe { NonNull::new_unchecked(p) });
+                let result = list.buckets[len - 1].pop();
             for ptr in bucket.drain(..) {
                 unsafe {
-                    alloc::alloc::dealloc(ptr as *mut u8, layout);
+                    alloc::alloc::dealloc(ptr.as_ptr() as *mut u8, layout);
                 }
             }

Also applies to: 125-127, 146-148

🤖 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 62 - 63, Change the buckets
field from Vec<*mut PyObject> to Vec<NonNull<PyObject>> (keeping the array type
[Vec<...>; Self::MAXSAVESIZE]) and update all places that push or construct
entries (e.g., where raw pointers are inserted) to create NonNull values via
NonNull::new(ptr).expect("non-null pointer") or otherwise handle the Option
instead of storing raw *mut; likewise replace the unchecked conversion at its
usage site with NonNull::as_ptr() (or .get()) to obtain a *mut when needed and
adjust any initializations/clears to work with NonNull<PyObject> instead of
*mut.
🤖 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/object/core.rs`:
- Around line 2091-2119: Currently code calls T::freelist_pop(&payload) whenever
!has_dict && !is_heaptype, but that lets shared-Rust-payload subtypes (e.g.
structseq) drain base-type freelists and then get deallocated on type mismatch;
fix by only probing the freelist when the requested PyType exactly matches the
freelist bucket or by passing the requested typ into the freelist pop so it can
decline non-exact matches. Concretely, update the condition around the cached
lookup to require an exact-type match (use the existing typ variable) or change
T::freelist_pop signature to T::freelist_pop(&payload, &typ) and have it return
None if the bucket’s stored typ != typ; then remove the mismatch deallocation
path (or guard it) so freelist entries for other PyTypes are never popped.

---

Nitpick comments:
In `@crates/vm/src/builtins/tuple.rs`:
- Around line 62-63: Change the buckets field from Vec<*mut PyObject> to
Vec<NonNull<PyObject>> (keeping the array type [Vec<...>; Self::MAXSAVESIZE])
and update all places that push or construct entries (e.g., where raw pointers
are inserted) to create NonNull values via NonNull::new(ptr).expect("non-null
pointer") or otherwise handle the Option instead of storing raw *mut; likewise
replace the unchecked conversion at its usage site with NonNull::as_ptr() (or
.get()) to obtain a *mut when needed and adjust any initializations/clears to
work with NonNull<PyObject> instead of *mut.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 0a5e6be4-676c-4497-8b86-e2f0b1a131e7

📥 Commits

Reviewing files that changed from the base of the PR and between 6be1e32 and 8a56dd5.

📒 Files selected for processing (10)
  • 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/range.rs
  • crates/vm/src/builtins/slice.rs
  • crates/vm/src/builtins/tuple.rs
  • crates/vm/src/object/core.rs
  • crates/vm/src/object/payload.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/vm/src/builtins/float.rs

@youknowone youknowone force-pushed the freelist-more-types branch 2 times, most recently from 417dde4 to ad7d629 Compare March 6, 2026 11:46
@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

Code has been automatically formatted

The code in this PR has been formatted using:

  • cargo fmt --all
    Please pull the latest changes before pushing again:
git pull origin freelist-more-types

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

📦 Library Dependencies

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

[ ] lib: cpython/Lib/asyncio
[ ] test: cpython/Lib/test/test_asyncio (TODO: 37)

dependencies:

  • asyncio (native: _asyncio, _overlapped, _pyrepl.console, _pyrepl.main, _pyrepl.simple_interact, _remote_debugging, _winapi, asyncio.tools, base_events, collections.abc, concurrent.futures, coroutines, errno, events, exceptions, futures, graph, itertools, locks, log, math, msvcrt, protocols, queues, readline, runners, streams, sys, taskgroups, tasks, threads, time, timeouts, transports, unix_events, windows_events)
    • collections (native: _collections, _weakref, itertools, sys)
    • logging (native: atexit, collections.abc, email.message, email.utils, errno, http.client, logging.handlers, multiprocessing.queues, select, sys, time, urllib.parse, win32evtlog, win32evtlogutil)
    • site (native: _io, _pyrepl.main, _pyrepl.pager, _pyrepl.readline, _pyrepl.unix_console, _pyrepl.windows_console, atexit, builtins, errno, readline, sitecustomize, sys, usercustomize)
    • tokenize (native: _tokenize, builtins, itertools, sys)
    • _colorize, argparse, ast, contextlib, contextvars, dataclasses, enum, functools, heapq, inspect, io, linecache, os, reprlib, rlcompleter, selectors, signal, socket, ssl, stat, struct, subprocess, tempfile, threading, traceback, types, warnings, weakref

dependent tests: (7 tests)

  • asyncio: test_asyncio test_contextlib_async test_inspect test_logging test_os test_sys_settrace test_unittest

[x] test: cpython/Lib/test/test_fork1.py

dependencies:

dependent tests: (no tests depend on fork1)

[x] lib: cpython/Lib/os.py
[ ] test: cpython/Lib/test/test_os.py (TODO: 1)
[x] test: cpython/Lib/test/test_popen.py

dependencies:

  • os

dependent tests: (169 tests)

  • os: test___all__ test__osx_support test_argparse test_ast test_asyncio test_atexit test_base64 test_baseexception test_bdb test_bool test_buffer test_builtin test_bytes test_bz2 test_c_locale_coercion test_calendar test_cmd_line test_cmd_line_script test_codecs test_compile test_compileall test_concurrent_futures test_configparser test_contextlib test_ctypes test_dbm test_dbm_dumb test_dbm_sqlite3 test_decimal test_devpoll test_doctest test_dtrace test_eintr test_email test_ensurepip test_enum test_epoll test_exception_hierarchy test_exceptions test_faulthandler test_fcntl test_file test_file_eintr test_filecmp test_fileinput test_fileio test_float test_fnmatch test_fork1 test_fractions test_fstring test_ftplib test_future_stmt test_genericalias test_genericpath test_getpass test_gettext test_glob test_graphlib test_gzip test_hash test_hashlib test_http_cookiejar test_httplib test_httpservers test_imaplib test_importlib test_inspect test_io test_ioctl test_json test_kqueue test_largefile test_launcher test_linecache test_locale test_logging test_lzma test_mailbox test_marshal test_math test_mimetypes test_mmap test_msvcrt test_multiprocessing_fork test_multiprocessing_forkserver test_multiprocessing_main_handling test_multiprocessing_spawn test_netrc test_ntpath test_openpty test_optparse test_os test_pathlib test_pkg test_pkgutil test_platform test_plistlib test_poll test_popen test_posix test_posixpath test_pty test_py_compile test_pydoc test_pyexpat test_random test_regrtest test_repl test_reprlib test_robotparser test_runpy test_sax test_script_helper test_selectors test_shelve test_shutil test_signal test_site test_smtpnet test_socket test_socketserver test_sqlite3 test_ssl test_stat test_string_literals test_structseq test_subprocess test_support test_sys test_sysconfig test_tabnanny test_tarfile test_tempfile test_termios test_thread test_threading test_threadsignals test_time test_tokenize test_tools test_trace test_tty test_typing test_unicode_file test_unicode_file_functions test_unittest test_univnewlines test_urllib test_urllib2 test_urllib2_localnet test_urllib2net test_urllibnet test_uuid test_venv test_wait3 test_wait4 test_wave test_webbrowser test_winapi test_winconsoleio test_winreg test_winsound test_wsgiref test_xml_etree test_zipfile test_zipimport test_zoneinfo test_zstd

[x] lib: cpython/Lib/threading.py
[x] lib: cpython/Lib/_threading_local.py
[ ] test: cpython/Lib/test/test_threading.py (TODO: 16)
[ ] test: cpython/Lib/test/test_threadedtempfile.py
[ ] test: cpython/Lib/test/test_threading_local.py (TODO: 3)

dependencies:

  • threading

dependent tests: (148 tests)

  • threading: test_android test_asyncio test_bz2 test_code test_concurrent_futures test_contextlib test_ctypes test_decimal test_docxmlrpc test_email test_enum test_fork1 test_frame test_ftplib test_functools test_gc test_hashlib test_httplib test_httpservers test_imaplib test_importlib test_inspect test_io test_itertools test_largefile test_linecache test_logging test_memoryview test_opcache test_pathlib test_poll test_queue test_robotparser test_sched test_signal test_smtplib test_socket test_socketserver test_sqlite3 test_ssl test_subprocess test_super test_sys test_syslog test_termios test_threadedtempfile test_threading test_threading_local test_time test_urllib2_localnet test_weakref test_winreg test_wsgiref test_xmlrpc test_zstd
    • asyncio: test_asyncio test_contextlib_async test_os test_sys_settrace test_unittest
    • bdb: test_bdb
    • concurrent.futures._base: test_concurrent_futures
    • concurrent.futures.process: test_compileall test_concurrent_futures
    • concurrent.futures.thread: test_genericalias
    • dummy_threading: test_dummy_threading
    • http.cookiejar: test_http_cookiejar test_urllib2
      • urllib.request: test_pathlib test_pydoc test_sax test_site test_urllib test_urllib2net test_urllibnet
    • importlib.util: test_ctypes test_doctest test_importlib test_pkgutil test_py_compile test_reprlib test_runpy test_zipfile test_zipimport
      • py_compile: test_argparse test_cmd_line_script test_importlib test_multiprocessing_main_handling
      • pyclbr: test_pyclbr
      • sysconfig: test_c_locale_coercion test_dtrace test_launcher test_osx_env test_posix test_pyexpat test_regrtest test_support test_sysconfig test_tools test_venv
      • zipfile: test_shutil test_zipapp test_zipfile test_zipfile64
    • logging: test_unittest
      • hashlib: test_hmac test_tarfile test_unicodedata
    • multiprocessing: test_fcntl test_re
    • queue: test_dummy_thread
    • subprocess: test_atexit test_audit test_cmd_line test_ctypes test_faulthandler test_file_eintr test_gzip test_json test_msvcrt test_ntpath test_platform test_plistlib test_quopri test_repl test_script_helper test_select test_tempfile test_traceback test_unittest test_utf8_mode test_wait3 test_webbrowser
      • ctypes.util: test_ctypes
      • ensurepip: test_ensurepip
      • platform: test__locale test__osx_support test_baseexception test_builtin test_cmath test_math test_mimetypes
    • sysconfig:
      • trace: test_trace
    • zipfile:
      • shutil: test_filecmp test_glob test_string_literals test_unicode_file test_zoneinfo

Legend:

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

@youknowone youknowone force-pushed the freelist-more-types branch 4 times, most recently from b7513c1 to bbc1d7f Compare March 8, 2026 14:11
Implement PyTuple freelist matching tuples[PyTuple_MAXSAVESIZE]:
- TupleFreeList with 20 per-size buckets (sizes 1..=20, 2000 capacity each)
- freelist_push uses pre-clear size hint for correct bucket selection
- freelist_pop takes &Self payload to select bucket by size
- Type guard in new_ref handles structseq types sharing PyTuple vtable
- Add pyinner_layout<T>() helper for custom freelist Drop impls
- Update freelist_pop/push signatures across all freelist types
Move exact-type filtering from freelist_pop implementations to the
single call-site in new_ref. This prevents structseq and other subtypes
from popping tuple freelist entries entirely, rather than popping and
then deallocating on type mismatch.

Add Context::try_genesis() that returns None during bootstrap to avoid
deadlock when genesis() is called during Context initialization.
Remove typ parameter from freelist_push trait signature. The exact type
check is now done once at the call-site alongside the heaptype check,
simplifying all freelist_push implementations.
By calling freelist_push before tp_clear, the payload is still intact
and can be read directly (e.g. tuple element count for bucket selection).
This eliminates freelist_hint and the hint parameter entirely.
@youknowone youknowone force-pushed the freelist-more-types branch from bbc1d7f to c32f31b Compare March 9, 2026 02:45
@youknowone youknowone merged commit 2266ba7 into RustPython:main Mar 9, 2026
13 checks passed
@youknowone youknowone deleted the freelist-more-types branch March 9, 2026 13:34
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.

1 participant