gh-155151: Check the recursion limit in CALL_EX_PY and CALL_KW_BOUND_METHOD - #155405
gh-155151: Check the recursion limit in CALL_EX_PY and CALL_KW_BOUND_METHOD#155405shoutoutuoadi325 wants to merge 2 commits into
Conversation
…BOUND_METHOD The generic CALL_FUNCTION_EX and CALL_KW reach start_frame, which calls _Py_EnterRecursivePy() and raises RecursionError before the callee runs. Their specialized forms end in _PUSH_FRAME, which decrements py_recursion_remaining without checking it. So once a call site had been warmed up, the callee was entered and returned normally where the unspecialized instruction raised — warming a call site changed whether the target function executed. CALL_KW_PY and CALL_BOUND_METHOD_EXACT_ARGS already guard their frame push with _CHECK_RECURSION_REMAINING. This adds it to the two that were missing it.
There was a problem hiding this comment.
Pull request overview
This PR fixes a behavioral mismatch at the Python recursion limit between generic call opcodes (CALL_FUNCTION_EX, CALL_KW) and their specialized forms (CALL_EX_PY, CALL_KW_BOUND_METHOD). Previously, once a call site specialized, the specialized fast-path could enter and run the callee in situations where the generic opcode would raise RecursionError before the callee executed.
Changes:
- Added
_CHECK_RECURSION_REMAININGguards to theCALL_EX_PYandCALL_KW_BOUND_METHODmacros so low recursion budget triggers deoptimization back to the generic opcode. - Regenerated interpreter generated code (
generated_cases.c.h) and opcode macro metadata to reflect the new uop sequence. - Added regression tests ensuring warmed (specialized) call sites do not execute the callee at the recursion boundary, plus a NEWS entry.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| Python/bytecodes.c | Inserts _CHECK_RECURSION_REMAINING into the two specialized call macros that could previously bypass the recursion check. |
| Python/generated_cases.c.h | Regenerated cases include the corresponding deopt-on-low-recursion blocks for the specialized opcodes. |
| Include/internal/pycore_opcode_metadata.h | Regenerated macro expansion metadata updates uop counts/sequences for the affected opcodes. |
| Lib/test/test_opcache.py | Adds regression tests to ensure specialization does not change recursion-limit behavior (callee must not run). |
| Misc/NEWS.d/next/Core_and_Builtins/2026-08-09-05-10-00.gh-issue-155151.b2A2eQ.rst | Documents the behavioral fix in the NEWS entry. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file should have been included in the original commit but was missed.
|
Identical (the test!) to #155272 and fully AI generated, I am closing this. PLease don't open PRs on advanced topics unless you (as a human, without an LLM) are able to do the same. I rather have someone who KNOWS the interpreter itself to make the work and the tests. Should you continue open fully-AI generated PRs, we will restrict your access to our repositories as it goes against the fair use of AI tools. |
Fixes #155151
Problem
The generic
CALL_FUNCTION_EXandCALL_KWreachstart_frame, which calls_Py_EnterRecursivePy()and raisesRecursionErrorbefore the callee runs.Their specialized forms end in
_PUSH_FRAME, which decrementspy_recursion_remainingwithout checking it. So once a call site had beenwarmed up, the callee was entered and returned normally where the
unspecialized instruction raised — warming a call site changed whether the
target function executed.
Solution
CALL_KW_PYandCALL_BOUND_METHOD_EXACT_ARGSalready guard their frame pushwith
_CHECK_RECURSION_REMAINING. This adds it to the two that were missingit:
CALL_EX_PYandCALL_KW_BOUND_METHOD.Changes
_CHECK_RECURSION_REMAININGtoCALL_EX_PYandCALL_KW_BOUND_METHODmacros inPython/bytecodes.cLib/test/test_opcache.pyTesting
test_recursion_check_for_call_ex_pyandtest_recursion_check_for_call_kw_bound_methodtestsRecursionErrorbefore entering the callee when the recursion limit is reached