Skip to content

Align codegen metadata with CPython#7952

Merged
youknowone merged 1 commit into
RustPython:mainfrom
youknowone:bytecode-parity
May 23, 2026
Merged

Align codegen metadata with CPython#7952
youknowone merged 1 commit into
RustPython:mainfrom
youknowone:bytecode-parity

Conversation

@youknowone
Copy link
Copy Markdown
Member

@youknowone youknowone commented May 22, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Improved float formatting tie-breaking and NaN equality for constants
    • More accurate source-line/column and exception-table location info
    • Fixes to async-generator behavior and StopIteration/error messages
    • Stronger source-encoding detection and BOM diagnostics
  • Chores

    • Updated spell-check dictionary with five tokens
    • Various codegen, bytecode, and tooling refinements
  • Tests

    • Multiple tests previously marked as expected failures are now enabled and run normally

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

Adds lineno_override and a line-only sentinel to codegen IR and preserves it through cloning/jump-threading; introduces ASYNC_GENERATOR flag and integrates it into VM fast-paths and frame handling; refines SymbolTable for methods and type-parameter defaults; aligns float formatting/to_hex with CPython; normalizes PEP 263 encodings; and updates spellfile and dis_dump.

Changes

IR Location & Optimization

Layer / File(s) Summary
Location sentinel & instruction metadata
crates/codegen/src/ir.rs
Adds LINE_ONLY_LOCATION_OVERRIDE, new InstructionInfo preservation flags, Block.conditional_ifexp_orelse_entry, and extends core location utilities to carry/propagate lineno_override.
Finalize/passes and block traversal
crates/codegen/src/ir.rs
Reorders finalize_code phases, adds block_next_order() traversal, and changes many folding passes to single forward scans in CFG order.
STORE_FAST_STORE_FAST/TO_BOOL propagation
crates/codegen/src/ir.rs
Preserves fused-store locations into following jumps, adds post-override propagation passes to propagate locations for fused/unary/TO_BOOL patterns.
Linetable & varint handling
crates/codegen/src/ir.rs
Derives column/end-column encoding from lineno_override, emits line-only entries when appropriate, and encodes negative cols as zero offsets.
Exception table & jump normalization
crates/codegen/src/ir.rs
Refactors exception-table generation to per-instruction context, detects conditional-jump reuse patterns, and updates normalize_jumps/jump-threading to carry lineno_override.
Cloning/duplication and NOP removal
crates/codegen/src/ir.rs
Updates many late-CFG transformations (return duplication, inline returns, shared jump-back duplicates, empty exit materialization) to overwrite cloned instruction locations including lineno_override.

ASYNC_GENERATOR Code Flag Support

Layer / File(s) Summary
CodeFlags and constant equality
crates/compiler-core/src/bytecode.rs
Adds ASYNC_GENERATOR = 0x0200 and FUTURE_ANNOTATIONS = 0x1000000; tightens ConstantData Float/Complex PartialEq to treat NaNs as unequal.
Function invocation and fast-paths
crates/vm/src/builtins/function.rs
Adds is_async_gen handling in invoke_with_locals, constructs PyAsyncGen, disables datastack-backed frames and exact-args/vectorcall simple fast paths for async generators.
Frame init and error messages
crates/vm/src/frame.rs
Includes ASYNC_GENERATOR when initializing prev_line and selects async-generator-specific StopIteration error text.

Symbol Table: methods & type-params

Layer / File(s) Summary
Method scope detection
crates/codegen/src/symboltable.rs
Adds pub is_method: bool, initializes it, and computes it in enter_scope when function-like scopes appear inside class blocks.
Type-parameter defaults
crates/codegen/src/symboltable.rs
Extends enter_type_param_block with has_defaults/has_kwdefaults, conditionally registers .defaults/.kwdefaults, and updates generic-call sites.
Annotation-scope nesting
crates/codegen/src/symboltable.rs
Refines enter_annotation_scope to compute synthetic __annotate__ nesting from current scope properties.
Comprehension named-expression cleanup
crates/codegen/src/symboltable.rs
Removes add_varname_to_scope and related conditional varname updates for named expressions in comprehensions.

Float literal formatting & to_hex

Layer / File(s) Summary
Scientific-notation tie handling
crates/literal/src/float.rs
Adds helpers to prefer CPython tie-digit choices, routes one scientific-notation path through them, and adds unit tests for specific powers of two.
to_hex reimplementation
crates/literal/src/float.rs
Reimplements to_hex using f64::to_bits() with explicit exponent/fraction extraction and updates tests for specific bit patterns.

Source encoding & stdlib

Layer / File(s) Summary
Encoding normalization
crates/vm/src/stdlib/builtins.rs
Adds normalize_source_encoding to map encoding cookie names to canonical CPython forms and updates PEP 263 extraction to return normalized names.
BOM error message
crates/vm/src/stdlib/builtins.rs
Changes BOM-with-non-UTF-8 SyntaxError message to include detected encoding string.

Supporting updates

Layer / File(s) Summary
cspell tokens
.cspell.dict/cpython.txt
Adds tokens: atext, ishidden, kwonlydefaults, ploc, stringized.
dis_dump simplification
scripts/dis_dump.py
Removes struct import and _normalize_const_repr helper; switches LOAD_CONST normalization to generic _normalize_argrepr(repr(...)) and simplifies instruction argrepr flow.
Test decorators
Lib/test/*
Removes many @unittest.expectedFailure decorators across tests so they execute as normal tests.

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly Related PRs

🐰 I hop on bytes and carry line-by-line,

Async gens bloom, and floats round just fine.
Symbols learn "method", encodings tidy their name,
Tests wake from expected-failures—no longer the same.
A rabbit applauds this tidy bytecode rhyme.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.49% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the pull request, which is to align codegen metadata and bytecode handling with CPython, covering changes across IR, symbol tables, bytecode flags, float formatting, and VM behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

📦 Library Dependencies

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

[ ] test: cpython/Lib/test/test_structseq.py

dependencies:

dependent tests: (no tests depend on structseq)

[x] lib: cpython/Lib/py_compile.py
[x] test: cpython/Lib/test/test_py_compile.py (TODO: 1)

dependencies:

  • py_compile

dependent tests: (42 tests)

  • py_compile: test_argparse test_cmd_line_script test_compileall test_importlib test_modulefinder test_multiprocessing_main_handling test_py_compile test_pydoc test_runpy
    • zipfile: test_pkgutil test_shutil test_zipapp test_zipfile test_zipfile64 test_zipimport test_zipimport_support
      • importlib.metadata: test_importlib test_zoneinfo
      • shutil: test_bz2 test_ctypes test_filecmp test_glob test_httpservers test_importlib test_inspect test_largefile test_launcher test_logging test_os test_peg_generator test_reprlib test_sax test_site test_string_literals test_subprocess test_support test_sysconfig test_tarfile test_tempfile test_traceback test_unicode_file test_venv

[x] test: cpython/Lib/test/test_float.py (TODO: 4)
[x] test: cpython/Lib/test/test_strtod.py (TODO: 2)

dependencies:

dependent tests: (no tests depend on float)

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

dependencies:

dependent tests: (233 tests)

  • sys: regrtestdata test___all__ test__colorize test__locale test__osx_support test_android test_annotationlib test_argparse test_array test_asdl_parser 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_class test_clinic 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_generated_cases test_generators test_genericpath test_genexps 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_perfmaps 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_strptime 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_cache 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_xpickle test_zipapp test_zipfile test_zipfile64 test_zipimport test_zipimport_support test_zlib

[x] lib: cpython/Lib/socketserver.py
[ ] test: cpython/Lib/test/test_socketserver.py (TODO: 1)

dependencies:

  • socketserver

dependent tests: (11 tests)

  • socketserver: test_imaplib test_logging test_socketserver test_wsgiref
    • http.server: test_httpservers test_robotparser test_urllib2_localnet test_xmlrpc
      • pydoc: test_enum test_pydoc
      • xmlrpc.server: test_docxmlrpc

[ ] test: cpython/Lib/test/test_timeout.py

dependencies:

dependent tests: (no tests depend on timeout)

[x] lib: cpython/Lib/inspect.py
[ ] test: cpython/Lib/test/test_inspect (TODO: 33)

dependencies:

  • inspect

dependent tests: (90 tests)

  • inspect: test_abc test_argparse test_asyncgen test_buffer test_builtin test_clinic test_code test_collections test_coroutines test_decimal test_enum test_functools test_generators test_grammar test_inspect test_monitoring test_ntpath test_operator test_patma test_posixpath test_pydoc test_signal test_sqlite3 test_traceback test_type_annotations test_types test_typing test_unittest test_yield_from test_zipimport test_zipimport_support test_zoneinfo
    • ast: test_ast test_compile test_compiler_codegen test_dis test_fstring test_future_stmt test_peepholer test_peg_generator test_site test_ssl test_type_comments test_ucn test_unparse
      • annotationlib: test_annotationlib test_reprlib test_type_params
      • dbm.dumb: test_dbm_dumb
      • pyclbr: test_pyclbr
      • traceback: test_asyncio test_code_module test_contextlib test_contextlib_async test_dictcomps test_exceptions test_http_cookiejar test_importlib test_iter test_listcomps test_pyexpat test_setcomps test_socket test_subprocess test_sys test_threadedtempfile test_threading test_unittest test_with
    • asyncio: test_asyncio test_logging test_os test_sys_settrace
    • bdb: test_bdb
    • cmd: test_cmd
    • dataclasses: test__colorize test_copy test_ctypes test_genericalias test_pprint test_regrtest
      • pprint: test_htmlparser test_sys_setprofile
    • importlib.metadata: test_importlib
    • pkgutil: test_pkgutil test_runpy
    • pydoc:
      • xmlrpc.server: test_docxmlrpc test_xmlrpc
    • rlcompleter: test_rlcompleter
    • trace: test_trace

[ ] test: cpython/Lib/test/test_exceptions.py (TODO: 23)
[ ] test: cpython/Lib/test/test_baseexception.py
[x] test: cpython/Lib/test/test_except_star.py (TODO: 1)
[ ] test: cpython/Lib/test/test_exception_group.py (TODO: 3)
[x] test: cpython/Lib/test/test_exception_hierarchy.py (TODO: 2)
[x] test: cpython/Lib/test/test_exception_variations.py

dependencies:

dependent tests: (no tests depend on exception)

[x] test: cpython/Lib/test/test_compile.py (TODO: 14)
[x] test: cpython/Lib/test/test_compiler_assemble.py
[x] test: cpython/Lib/test/test_compiler_codegen.py
[x] test: cpython/Lib/test/test_peepholer.py (TODO: 4)

dependencies:

dependent tests: (no tests depend on compile)

[x] lib: cpython/Lib/dis.py
[x] test: cpython/Lib/test/test_dis.py (TODO: 9)

dependencies:

  • dis

dependent tests: (73 tests)

  • dis: test__opcode test_ast test_code test_compile test_compiler_assemble test_dis test_dtrace test_fstring test_inspect test_monitoring test_opcache test_patma test_peepholer test_positional_only_arg test_type_cache
    • bdb: test_bdb
    • inspect: test_abc test_argparse test_asyncgen test_buffer test_builtin test_clinic test_collections test_coroutines test_decimal test_enum test_functools test_generators test_grammar test_ntpath test_operator test_posixpath test_pydoc test_signal test_sqlite3 test_traceback test_type_annotations test_types test_typing test_unittest test_yield_from test_zipimport test_zipimport_support test_zoneinfo
      • ast: test_compiler_codegen test_future_stmt test_peg_generator test_site test_ssl test_type_comments test_ucn test_unparse
      • asyncio: test_asyncio test_logging test_os test_sys_settrace test_unittest
      • cmd: test_cmd
      • dataclasses: test__colorize test_copy test_ctypes test_genericalias test_pprint test_regrtest
      • importlib.metadata: test_importlib
      • pkgutil: test_pkgutil test_runpy
      • rlcompleter: test_rlcompleter
      • trace: test_trace
      • xmlrpc.server: test_docxmlrpc test_xmlrpc
    • modulefinder: test_importlib test_modulefinder

[ ] lib: cpython/Lib/unittest
[x] test: cpython/Lib/test/test_unittest (TODO: 12)

dependencies:

  • unittest (native: _io, _log, async_case, builtins, case, loader, main, os.path, result, runner, signals, suite, sys, time, unittest.util, util)
    • 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)
    • warnings (native: _contextvars, _thread, _warnings, builtins, sys)
    • _colorize, annotationlib, argparse, contextlib, contextvars, dataclasses, difflib, fnmatch, functools, inspect, io, logging, os, pkgutil, pprint, re, signal, threading, traceback, types, weakref

dependent tests: (395 tests)

  • unittest: regrtestdata test___all__ test__colorize test__locale test__opcode test__osx_support test_abc test_abstract_numbers test_android test_annotationlib test_apple test_argparse test_array test_asdl_parser test_ast test_asyncgen test_asyncio test_atexit test_audit test_augassign test_base64 test_baseexception test_bdb test_bigaddrspace test_bigmem test_binascii test_binop test_bisect test_bool test_buffer test_bufio test_builtin test_bytes test_bz2 test_c_locale_coercion test_calendar test_call test_charmapcodec test_class test_clinic test_cmath test_cmd test_cmd_line test_cmd_line_script test_code test_code_module test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_iso2022 test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codecs test_codeop test_collections test_colorsys test_compare test_compile test_compileall test_complex test_concurrent_futures test_configparser test_contains test_context test_contextlib test_contextlib_async test_copy test_copyreg test_coroutines test_csv test_ctypes test_datetime test_dbm test_dbm_dumb test_dbm_sqlite3 test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_devpoll test_dict test_dictcomps test_dictviews test_difflib test_dis test_doctest test_doctest2 test_docxmlrpc test_dtrace test_dummy_thread test_dummy_threading test_dynamic test_dynamicclassattribute test_eintr test_email test_ensurepip test_enum test_enumerate test_eof test_epoll test_errno test_except_star test_exception_group test_exception_hierarchy test_exception_variations test_exceptions test_extcall test_faulthandler test_fcntl test_file test_file_eintr test_filecmp test_fileinput test_fileio test_finalization test_float test_flufl test_fnmatch test_fork1 test_format test_fractions test_frame test_frozen test_fstring test_ftplib test_funcattrs test_functools test_future_stmt test_gc test_generated_cases test_generator_stop test_generators test_genericalias test_genericclass test_genericpath test_genexps test_getopt test_getpass test_gettext test_glob test_global test_grammar test_graphlib test_grp test_gzip test_hash test_hashlib test_heapq test_hmac test_html test_htmlparser test_http_cookiejar test_http_cookies test_httplib test_httpservers test_imaplib test_importlib test_index test_inspect test_int test_int_literal test_io test_ioctl test_ipaddress test_isinstance test_iter test_iterlen test_itertools test_json test_keyword test_keywordonlyarg test_kqueue test_largefile test_launcher test_linecache test_list test_listcomps test_locale test_logging test_long test_longexp test_lzma test_mailbox test_marshal test_math test_math_property test_memoryio test_memoryview test_metaclass test_mimetypes test_minidom test_mmap test_modulefinder test_monitoring test_msvcrt test_multiprocessing_fork test_multiprocessing_forkserver test_multiprocessing_main_handling test_multiprocessing_spawn test_named_expressions test_netrc test_ntpath test_nturl2path test_numeric_tower test_opcache test_opcodes test_openpty test_operator test_optimizer test_optparse test_ordered_dict test_os test_osx_env test_pathlib test_patma test_peepholer test_peg_generator test_pep646_syntax test_perfmaps test_pickle test_picklebuffer test_pickletools test_pkg test_pkgutil test_platform test_plistlib test_poll test_popen test_poplib test_positional_only_arg test_posix test_posixpath test_pow test_pprint test_print test_property test_pty test_pulldom test_pwd test_py_compile test_pyclbr test_pydoc test_pyexpat test_queue test_quopri test_raise test_random test_range test_re test_regrtest test_repl test_reprlib test_resource test_richcmp test_rlcompleter test_robotparser test_runpy test_sax test_sched test_scope test_script_helper test_secrets test_select test_selectors test_set test_setcomps test_shelve test_shlex test_shutil test_signal test_site test_slice test_smtplib test_smtpnet test_socket test_socketserver test_sort test_sqlite3 test_ssl test_stable_abi_ctypes test_stat test_statistics test_str test_strftime test_string test_string_literals test_stringprep test_strptime test_strtod test_struct test_structseq test_subclassinit test_subprocess test_sundry test_super test_support test_symtable test_syntax test_sys test_sys_setprofile test_sys_settrace test_sysconfig test_syslog test_tabnanny test_tarfile test_tempfile test_termios test_textwrap test_thread test_threadedtempfile test_threading test_threading_local test_threadsignals test_time test_timeit test_timeout test_tokenize test_tomllib test_tools test_trace test_traceback test_tstring test_tty test_tuple test_type_aliases test_type_annotations test_type_cache test_type_comments test_type_params test_typechecks test_types test_typing test_ucn test_unary test_unicode_file test_unicode_file_functions test_unicode_identifiers test_unicodedata test_unittest test_univnewlines test_unpack test_unpack_ex test_unparse test_urllib test_urllib2 test_urllib2_localnet test_urllib2net test_urllib_response test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_utf8_mode test_utf8source test_uuid test_venv test_wait3 test_wait4 test_wave test_weakref test_weakset test_webbrowser test_winapi test_winconsoleio test_winreg test_winsound test_with test_wmi test_wsgiref test_xml_dom_minicompat test_xml_dom_xmlbuilder test_xml_etree test_xml_etree_c test_xmlrpc test_xpickle test_xxlimited test_xxtestfuzz test_yield_from test_zipapp test_zipfile test_zipfile64 test_zipimport test_zipimport_support test_zlib test_zoneinfo test_zstd

Legend:

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

@youknowone youknowone marked this pull request as ready for review May 22, 2026 13:55
Copy link
Copy Markdown
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
crates/codegen/src/ir.rs (1)

6632-6644: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve lineno_override on synthesized control-flow markers.

These initializers copy an existing source span but reset lineno_override to None. That drops Some(-1) / LINE_ONLY_LOCATION_OVERRIDE, so later instruction_lineno() and generate_linetable() treat the synthetic NotTaken / Nop as normal column-bearing instructions and change the emitted linetable.

Suggested fix
-                    lineno_override: None,
+                    lineno_override: last_ins.lineno_override,
-                        lineno_override: None,
+                        lineno_override: source.lineno_override,

Also applies to: 6677-6711, 7811-7820, 7848-7857

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/codegen/src/ir.rs` around lines 6632 - 6644, Synthesized control-flow
marker InstructionInfo instances (e.g., the NotTaken and Nop entries) currently
reset lineno_override to None, which drops special values like Some(-1); change
those initializers to preserve the source span's override by copying
last_ins.lineno_override (e.g., set lineno_override: last_ins.lineno_override)
so instruction_lineno()/generate_linetable() sees the original override; apply
the same change to all similar blocks that construct InstructionInfo (the ones
around NotTaken/Nop at the other locations mentioned).
crates/codegen/src/symboltable.rs (2)

1508-1514: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pass a real has_defaults predicate here.

Hard-coding true means every generic function gets a .defaults parameter, even when it has no positional defaults. That undercuts the new conditional registration in enter_type_param_block() and changes the type-parameter scope signature.

Suggested fix
                     self.enter_type_param_block(
                         &format!("<generic parameters of {}>", name.as_str()),
                         self.line_index_start(type_params.range),
                         false,
-                        true,
+                        Self::has_positional_defaults(parameters),
                         Self::has_kwonlydefaults(parameters),
                     )?;
fn has_positional_defaults(parameters: &ast::Parameters) -> bool {
    parameters
        .posonlyargs
        .iter()
        .chain(parameters.args.iter())
        .any(|arg| arg.default.is_some())
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/codegen/src/symboltable.rs` around lines 1508 - 1514, The call to
enter_type_param_block currently passes a hard-coded true for the has_defaults
predicate which forces every generic to get a .defaults parameter; replace that
literal with a real predicate like has_positional_defaults(parameters). Add a
helper function has_positional_defaults(&ast::Parameters) -> bool that returns
true if any posonlyargs or args have a default (iterate
parameters.posonlyargs.chain(parameters.args) and check arg.default.is_some()),
then call enter_type_param_block(..., Self::has_kwonlydefaults(parameters),
has_positional_defaults(parameters)) so the type-parameter scope signature and
conditional registration remain correct.

1126-1146: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Look through synthetic type-parameter scopes when setting is_method.

Generic class methods are entered under CompilerScope::TypeParams, so parent.typ == CompilerScope::Class misses cases like class C: def f[T](...). That leaves the new metadata false for generic methods.

Suggested fix
-        let is_method = parent.is_some_and(|table| {
-            table.typ == CompilerScope::Class
+        let is_method = parent.is_some_and(|table| {
+            (table.typ == CompilerScope::Class
+                || (table.typ == CompilerScope::TypeParams && table.can_see_class_scope))
                 && matches!(
                     typ,
                     CompilerScope::Function
                         | CompilerScope::AsyncFunction
                         | CompilerScope::Lambda
                         | CompilerScope::Comprehension
                 )
         });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/codegen/src/symboltable.rs` around lines 1126 - 1146, The is_method
check only looks at the immediate parent and misses synthetic
CompilerScope::TypeParams wrappers (so generic class methods under a TypeParams
scope are not detected); change the logic that computes is_method to walk up
self.tables backwards skipping any CompilerScope::TypeParams entries (e.g., find
the nearest ancestor whose typ != CompilerScope::TypeParams) and then test
whether that ancestor.typ == CompilerScope::Class and the new scope typ matches
function-like kinds (the existing match on CompilerScope::Function |
AsyncFunction | Lambda | Comprehension); update the local is_method computation
(the variable named is_method) to use that ancestor test instead of
parent.is_some_and(|table| table.typ == CompilerScope::Class).
🧹 Nitpick comments (1)
crates/compiler-core/src/bytecode.rs (1)

452-469: Clean build recommended for bytecode changes.

Since CodeFlags constants are being added, consider performing a clean build to ensure no stale bytecode artifacts remain. As per coding guidelines, run cargo fmt to format the code and cargo clippy to check for any new lints before completing the task.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/compiler-core/src/bytecode.rs` around lines 452 - 469, You added new
CodeFlags constants in the CodeFlags bitflags block (in bytecode.rs) which can
leave stale artifacts or style/lint issues; perform a clean rebuild and run
cargo fmt and cargo clippy: clean the build artifacts (cargo clean), build and
test (cargo build && cargo test) to ensure no stale bytecode or compilation
errors, run cargo fmt to apply formatting to
crates/compiler-core/src/bytecode.rs and the workspace, and run cargo clippy to
address any new lints introduced by the added flags (fix or silence issues
reported for CodeFlags, e.g., unused or naming lints).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/codegen/src/ir.rs`:
- Around line 6632-6644: Synthesized control-flow marker InstructionInfo
instances (e.g., the NotTaken and Nop entries) currently reset lineno_override
to None, which drops special values like Some(-1); change those initializers to
preserve the source span's override by copying last_ins.lineno_override (e.g.,
set lineno_override: last_ins.lineno_override) so
instruction_lineno()/generate_linetable() sees the original override; apply the
same change to all similar blocks that construct InstructionInfo (the ones
around NotTaken/Nop at the other locations mentioned).

In `@crates/codegen/src/symboltable.rs`:
- Around line 1508-1514: The call to enter_type_param_block currently passes a
hard-coded true for the has_defaults predicate which forces every generic to get
a .defaults parameter; replace that literal with a real predicate like
has_positional_defaults(parameters). Add a helper function
has_positional_defaults(&ast::Parameters) -> bool that returns true if any
posonlyargs or args have a default (iterate
parameters.posonlyargs.chain(parameters.args) and check arg.default.is_some()),
then call enter_type_param_block(..., Self::has_kwonlydefaults(parameters),
has_positional_defaults(parameters)) so the type-parameter scope signature and
conditional registration remain correct.
- Around line 1126-1146: The is_method check only looks at the immediate parent
and misses synthetic CompilerScope::TypeParams wrappers (so generic class
methods under a TypeParams scope are not detected); change the logic that
computes is_method to walk up self.tables backwards skipping any
CompilerScope::TypeParams entries (e.g., find the nearest ancestor whose typ !=
CompilerScope::TypeParams) and then test whether that ancestor.typ ==
CompilerScope::Class and the new scope typ matches function-like kinds (the
existing match on CompilerScope::Function | AsyncFunction | Lambda |
Comprehension); update the local is_method computation (the variable named
is_method) to use that ancestor test instead of parent.is_some_and(|table|
table.typ == CompilerScope::Class).

---

Nitpick comments:
In `@crates/compiler-core/src/bytecode.rs`:
- Around line 452-469: You added new CodeFlags constants in the CodeFlags
bitflags block (in bytecode.rs) which can leave stale artifacts or style/lint
issues; perform a clean rebuild and run cargo fmt and cargo clippy: clean the
build artifacts (cargo clean), build and test (cargo build && cargo test) to
ensure no stale bytecode or compilation errors, run cargo fmt to apply
formatting to crates/compiler-core/src/bytecode.rs and the workspace, and run
cargo clippy to address any new lints introduced by the added flags (fix or
silence issues reported for CodeFlags, e.g., unused or naming lints).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: c62028dd-45be-4d98-88b4-f81b2cf7f8e6

📥 Commits

Reviewing files that changed from the base of the PR and between f3b83ef and a8f9a90.

⛔ Files ignored due to path filters (9)
  • Lib/test/test_compile.py is excluded by !Lib/**
  • Lib/test/test_dis.py is excluded by !Lib/**
  • Lib/test/test_exceptions.py is excluded by !Lib/**
  • Lib/test/test_inspect/test_inspect.py is excluded by !Lib/**
  • Lib/test/test_py_compile.py is excluded by !Lib/**
  • Lib/test/test_strtod.py is excluded by !Lib/**
  • Lib/test/test_sys_settrace.py is excluded by !Lib/**
  • Lib/test/test_unittest/test_async_case.py is excluded by !Lib/**
  • crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__nested_double_async_with.snap is excluded by !**/*.snap
📒 Files selected for processing (10)
  • .cspell.dict/cpython.txt
  • crates/codegen/src/compile.rs
  • crates/codegen/src/ir.rs
  • crates/codegen/src/symboltable.rs
  • crates/compiler-core/src/bytecode.rs
  • crates/literal/src/float.rs
  • crates/vm/src/builtins/function.rs
  • crates/vm/src/frame.rs
  • crates/vm/src/stdlib/builtins.rs
  • scripts/dis_dump.py

Comment thread crates/codegen/src/ir.rs
Copy link
Copy Markdown
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/codegen/src/symboltable.rs (1)

1508-1514: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pass the real positional-default presence into the type-params scope.

Line 1512 hardcodes has_defaults to true, so a generic function with no positional defaults still gets a synthetic .defaults parameter. That changes the helper scope’s parameter metadata and can shift later synthetic slots away from CPython.

Suggested fix
                 if let Some(type_params) = type_params {
                     self.enter_type_param_block(
                         &format!("<generic parameters of {}>", name.as_str()),
                         self.line_index_start(type_params.range),
                         false,
-                        true,
+                        Self::has_defaults(parameters),
                         Self::has_kwonlydefaults(parameters),
                     )?;
                     self.scan_type_params(type_params)?;
                 }

Add a helper alongside has_kwonlydefaults:

fn has_defaults(parameters: &ast::Parameters) -> bool {
    parameters
        .posonlyargs
        .iter()
        .chain(parameters.args.iter())
        .any(|arg| arg.default.is_some())
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/codegen/src/symboltable.rs` around lines 1508 - 1514, The call to
enter_type_param_block currently hardcodes has_defaults to true causing
synthetic `.defaults` to be added even when no positional defaults exist; add a
helper fn has_defaults(parameters: &ast::Parameters) -> bool that checks
posonlyargs and args for any arg.default.is_some(), then replace the hardcoded
true in the enter_type_param_block invocation with
Self::has_defaults(parameters) (leave Self::has_kwonlydefaults(parameters)
as-is) so the type-params scope gets the correct positional-default presence;
update any visibility (impl Self) as needed where has_kwonlydefaults is defined.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/codegen/src/ir.rs`:
- Around line 4597-4599: debug_late_cfg_trace() must mirror finalize_code()
exactly by running the same constant-folding passes around peephole
optimization: add calls to fold_constants_per_block() before and after the
peephole/optimize steps so the trace includes the two new
fold_constants_per_block() passes that finalize_code() runs; ensure these new
calls are placed in the same order relative to optimize_build_tuple_unpack(),
eliminate_dead_stores(), apply_static_swaps(), and peephole_optimize() so the
trace output stays in lockstep with finalize_code().

In `@crates/compiler-core/src/bytecode.rs`:
- Around line 912-921: The PartialEq implementation for ConstantData currently
treats Float/Complex containing NaN as unequal to themselves, violating Rust's
Eq reflexivity; update ConstantData::PartialEq (the Float and Complex match
arms) to compare floats/complex parts by their bit-patterns (use to_bits()
equality for float components) so NaNs compare equal to themselves, and keep
impl Eq and the Hash impl unchanged; then move CPython's "don't merge NaNs"
behavior out of ConstantData into the constant interning/dedup layer (the
constant-key/wrapper or the interning/merge function) so that the interner
refuses to merge constants that contain NaNs instead of encoding that behavior
in ConstantData::PartialEq.

---

Outside diff comments:
In `@crates/codegen/src/symboltable.rs`:
- Around line 1508-1514: The call to enter_type_param_block currently hardcodes
has_defaults to true causing synthetic `.defaults` to be added even when no
positional defaults exist; add a helper fn has_defaults(parameters:
&ast::Parameters) -> bool that checks posonlyargs and args for any
arg.default.is_some(), then replace the hardcoded true in the
enter_type_param_block invocation with Self::has_defaults(parameters) (leave
Self::has_kwonlydefaults(parameters) as-is) so the type-params scope gets the
correct positional-default presence; update any visibility (impl Self) as needed
where has_kwonlydefaults is defined.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 24688510-8965-405d-895b-9187436f34e1

📥 Commits

Reviewing files that changed from the base of the PR and between a8f9a90 and 0961896.

⛔ Files ignored due to path filters (9)
  • Lib/test/test_compile.py is excluded by !Lib/**
  • Lib/test/test_dis.py is excluded by !Lib/**
  • Lib/test/test_exceptions.py is excluded by !Lib/**
  • Lib/test/test_inspect/test_inspect.py is excluded by !Lib/**
  • Lib/test/test_py_compile.py is excluded by !Lib/**
  • Lib/test/test_strtod.py is excluded by !Lib/**
  • Lib/test/test_sys_settrace.py is excluded by !Lib/**
  • Lib/test/test_unittest/test_async_case.py is excluded by !Lib/**
  • crates/stdlib/src/snapshots/rustpython_stdlib___opcode__tests__nested_double_async_with.snap is excluded by !**/*.snap
📒 Files selected for processing (10)
  • .cspell.dict/cpython.txt
  • crates/codegen/src/compile.rs
  • crates/codegen/src/ir.rs
  • crates/codegen/src/symboltable.rs
  • crates/compiler-core/src/bytecode.rs
  • crates/literal/src/float.rs
  • crates/vm/src/builtins/function.rs
  • crates/vm/src/frame.rs
  • crates/vm/src/stdlib/builtins.rs
  • scripts/dis_dump.py
✅ Files skipped from review due to trivial changes (1)
  • .cspell.dict/cpython.txt

Comment thread crates/codegen/src/ir.rs
Comment thread crates/compiler-core/src/bytecode.rs Outdated
Comment on lines 912 to 921
// Match CPython's _PyCode_ConstantKey(): NaN constants are not
// merged because nan != nan at the Python level.
(Float { value: a }, Float { value: b }) => {
!a.is_nan() && !b.is_nan() && a.to_bits() == b.to_bits()
}
(Complex { value: a }, Complex { value: b }) => {
if a.re.is_nan() || a.im.is_nan() || b.re.is_nan() || b.im.is_nan() {
return false;
}
a.re.to_bits() == b.re.to_bits() && a.im.to_bits() == b.im.to_bits()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🌐 Web query:

Does Rust's Eqtrait require reflexive equality, and what issues can occur if aHash/Eq key type compares unequal to itself for NaN values?

💡 Result:

Yes, Rust's Eq trait strictly requires reflexive equality [1][2]. Specifically, for any type that implements Eq, the relation must satisfy the following properties for all values a, b, and c: - Reflexivity: a == a must always be true [1][2]. - Symmetry: a == b implies b == a [1][2]. - Transitivity: a == b and b == c implies a == c [1][2]. While PartialEq only requires symmetry and transitivity, Eq acts as a marker trait (it has no methods of its own) that informs the compiler and other developers that the equality relation is a total equivalence relation, including reflexivity [1][2][3]. Issues with NaN and Hash/Eq Keys: Floating-point types like f32 and f64 do not implement Eq precisely because NaN (Not-a-Number) values are not equal to themselves (NaN!= NaN), which violates the reflexivity requirement [4][5]. If you were to force a type that compares unequal to itself (like a raw float) to be used as a key in a HashMap or HashSet, it would cause significant logic errors [6][7]: 1. Unreachable Data: HashMap and HashSet rely on the assumption that a key is equal to itself [6][7]. If you insert a value (like NaN) as a key, you may be unable to retrieve, remove, or even verify the existence of that entry later, because the lookup process will fail to find the key even if it is present in the map [4][5]. 2. Logic Errors: Violating the Eq contract is considered a logic error [1][6]. While it will not cause undefined behavior (memory safety issues) in safe Rust, it leads to unpredictable behavior in collections, such as silent data loss or the inability to manage stored items [6][7]. To use floating-point numbers as keys in Rust, the standard approach is to use a wrapper type (such as those provided by the ordered-float crate) that defines a total ordering and equality for floats, typically by treating all NaN values as equal to each other or by rejecting them entirely [4][8].

Citations:


Fix ConstantData’s Eq contract for NaN values.
Eq in Rust must be reflexive (a == a always). The current ConstantData PartialEq logic for Float/Complex makes NaN-bearing constants compare unequal to themselves (around 912–921), while impl Eq for ConstantData and its Hash impl remain in place (around 937 and 939–962). This violates Rust’s Eq/Hash assumptions and can break constant interning/dedup/lookups. Keep ConstantData’s equality reflexive and move CPython’s “don’t merge NaNs” behavior into the constant-key/wrapper or the interning/dedup logic instead of encoding it in ConstantData::PartialEq.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/compiler-core/src/bytecode.rs` around lines 912 - 921, The PartialEq
implementation for ConstantData currently treats Float/Complex containing NaN as
unequal to themselves, violating Rust's Eq reflexivity; update
ConstantData::PartialEq (the Float and Complex match arms) to compare
floats/complex parts by their bit-patterns (use to_bits() equality for float
components) so NaNs compare equal to themselves, and keep impl Eq and the Hash
impl unchanged; then move CPython's "don't merge NaNs" behavior out of
ConstantData into the constant interning/dedup layer (the constant-key/wrapper
or the interning/merge function) so that the interner refuses to merge constants
that contain NaNs instead of encoding that behavior in ConstantData::PartialEq.

@youknowone youknowone merged commit d3272e7 into RustPython:main May 23, 2026
27 checks passed
@youknowone youknowone deleted the bytecode-parity branch May 23, 2026 11:16
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