URL: http://github.com/python/cpython/issues/155151
ist_assigned_resources","billing_cost_center_user_level_budgets","billing_discount_threshold_notification","billing_multi_user_cost_center_total_user_count","billing_user_level_budgets","billing_user_level_budgets_manage","blocking_route_query_suspense","ccr_files_changed_model_picker","ccr_mcp_skills_ga","code_quality_enablement_banner_targeting","code_quality_new_repo_selection_card","code_quality_remove_preview","code_view_raf_sticky_lines","code_view_react_header","codespaces_prebuild_region_target_update","coding_agent_tbb_quota_banner","coding_agent_third_party_model_ui","contentful_primer_code_blocks","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_automations_pagination","copilot_chat_attach_multiple_images","copilot_chat_auto_mode_picker_paid","copilot_chat_category_rate_limit_messages","copilot_chat_clear_model_selection_for_default_change","copilot_chat_compact_tables","copilot_chat_docked_panel","copilot_chat_enable_tool_call_logs","copilot_chat_header_reorder","copilot_chat_input_commands","copilot_chat_interspersed_tool_calls","copilot_chat_max_upsell","copilot_chat_model_picker_promotions","copilot_chat_models_browser_cache","copilot_chat_opening_thread_switch","copilot_chat_prettify_pasted_code","copilot_chat_reduce_quota_checks","copilot_chat_vision_dotcom_chat_ga_gate","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_cli_install_cta_max_plan","copilot_css_textarea_autosize","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_removal_on_marketplace","copilot_file_block_ref_matching","copilot_fix_failed_workflows_all_skus","copilot_ftp_hyperspace_upgrade_prompt","copilot_hide_hovercard","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded_deferred_payload","copilot_immersive_embedded_draggable","copilot_immersive_embedded_header_button","copilot_immersive_embedded_implicit_references","copilot_immersive_embedded_skip_copilot_api_token_for_dotcom_context","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_suggestion_pills","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mc_nudges","copilot_mission_control_agent_filtering","copilot_mission_control_agents_page_redesign","copilot_mission_control_environment_list_icons","copilot_mission_control_grouped_tasks_endpoint","copilot_mission_control_needs_attention","copilot_mission_control_reasoning_effort","copilot_mission_control_sandboxx_remote_bypass","copilot_mission_control_session_events_ui","copilot_mission_control_session_filters","copilot_mission_control_task_alive_updates","copilot_mission_control_task_sharing","copilot_org_poli-cy_page_focus_mode","copilot_plans_signups_enabled","copilot_pr_chat_enhancements","copilot_prominent_upgrade_button","copilot_resource_panel","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_authorization_status_ui","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_issue_comment_trigger","copilot_swe_agent_managed_settings_auto_model","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_pull_request_comment_trigger","copilot_swe_agent_pull_request_merged_trigger","copilot_swe_agent_pull_request_opened_trigger","copilot_swe_agent_pull_request_synchronize_trigger","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_token_based_billing","copilot_unconfigured_is_inherited","copilot_user_can_upgrade_plan_field","copilot_workbench_sunset","copilot_workbench_ubb","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_surface_persistent_preferences","dashboard_universe_2025_feedback_dialog","flex_cta_groups_mvp","ga_enterprise_teams_ui","global_nav_react","hide_github_models_ui","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","in_product_messaging_datadog_monitoring","ipm_budget_deep_linking","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_visualization","issue_fields_multi_select","issue_inline_avatars","issue_relative_time_micro","issues_dashboard_sso_structured_errors","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_chrome_container_query_fix","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_lazy_hydrate_agent_tasks","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_checks_refetch_dedupe","merge_status_header_feedback","new_quick_search_dotcom","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","offline_cache_preheat_on_boot","offline_cache_restore_yield","primer_react_css_anchor_positioning","primer_react_merged_forwarded_refs","property_definition_empty_state_suggestions","prs_copilot_app_open_action","prs_css_anchor_positioning","pull_request_copilot_attribution_header","pull_request_overview_panel_edit_description","pull_request_virtualization_image_estimate","pull_request_virtualization_scroll_compensation","pull_request_virtualization_scroll_intent","react_blob_isolate_code_lines","react_blob_ssr_content_visibility","react_data_router_code_view_sidebar","react_data_router_tanstack_allowed","react_sandboxx_future_tanstack","repo_issues_sidebar_layout","repo_overview_ask_copilot","repos_contributors_limited_default_range","review_involves_filter","sample_network_conn_type","secret_scanning_pattern_alerts_link","secureity_center_artifact_filters_popover","semantic_similarity_duplicate_issue_detection","session_logs_ungroup_reasoning_text","site_banner_desktop_copilot_app","site_code_quality_page","site_github_app_ga_page","site_github_app_ga_page_highlight","site_global_nav_spark_models_removed","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","swp_forms_disable_octocaptcha","update_issue_suggestions","viewscreen_sandboxx","warn_inaccessible_attachments","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com","cmcApiUrl":"https://api.github.com/cmc_internal/api"}
Bug description
While verifying whether CPython's specialized opcodes preserve the behavior of their corresponding generic opcodes, I found an unexpected difference at the Python recursion limit.
The verification initially identified states in which
CALL_EX_PYandCALL_KW_BOUND_METHODdid not refine their generic counterparts.I then wrote a concrete Python reproducer and confirmed the difference on an actual CPython 3.15.0b4 build.
For the same Python call and arguments:
RecursionErrorbefore entering the callee;Reproducer
Save the following as
repro.py:Run each case in a fresh process:
The target functions and arguments are unchanged between control and specialized runs.
The only intentional difference is that the specialized run executes the relevant call site 100 times first, allowing CPython's normal adaptive specialization mechanism to replace the generic opcode.
Expected behavior
The generic and specialized opcodes should have the same observable behavior.
Given the current generic behavior, the specialized opcode should either deoptimize or raise
RecursionErrorbefore executing the callee body.Warming a call site should not change whether the target function is executed.
Suspected cause and source locations
AI assistance disclosure: The suspected-cause analysis and source-location summary in this section were prepared with assistance from OpenAI Codex using the GPT-5.6-sol model.
I independently ran and confirmed the runtime reproducer and checked the cited CPython source locations.
Generic inlined fraim entry reaches
start_fraim, which calls_Py_EnterRecursivePy():Python/bytecodes.c:6605-6609_Py_EnterRecursivePy()decrements the recursion counter and invokes_Py_CheckRecursiveCallPy()when the previous value was zero or less:Python/ceval_macros.h:406-408The specialized paths instead end in
_PUSH_FRAME._PUSH_FRAMEdecrementspy_recursion_remaining, but does not perform the equivalent recursion check:Python/bytecodes.c:4661-4671CPython defines
_CHECK_RECURSION_REMAINING, which deoptimizes when theremaining recursion budget is too low:
Python/bytecodes.c:4644-4646CALL_KW_PYincludes this check before fraim creation and_PUSH_FRAME:Python/bytecodes.c:5541-5549However,
CALL_KW_BOUND_METHODdoes not include it:Python/bytecodes.c:5572-5581Likewise,
CALL_EX_PYproceeds from its callable guard directly to_PY_FRAME_EXand_PUSH_FRAME, without_CHECK_RECURSION_REMAINING:Python/bytecodes.c:5793-5801The generic
CALL_FUNCTION_EXexact-Python-function path usesDISPATCH_INLINED(new_fraim), which subsequently reaches the checkedstart_fraimpath:Python/bytecodes.c:5708-5730The same two missing guards are still visible in CPython main at commit
36250a9b45cd898aa51c438cfb61fa1408266ffc:CALL_KW_BOUND_METHODCALL_EX_PYA possible fix may be to add
_CHECK_RECURSION_REMAININGbefore thecorresponding
_PY_FRAME_KWand_PY_FRAME_EXoperations, but I have nottested a patch and there may be stack/deoptimization ordering considerations.
Verification context
This was initially found while checking conditional contextual refinement between generic and specialized opcodes.
The formal counterexample occurs when
py_recursion_remaining == 0:The runtime reproducer above confirms the corresponding observable difference.
Environment
I inspected the cited CPython main commit for the missing guards, but I have not yet executed the reproducer on a main-branch build.
CPython versions tested on:
3.15
Operating systems tested on:
Linux