https://github.com/python/cpython/commit/83de667b25f9e0f3dd7a37b6f3d136a444cd6a0d
commit: 83de667b25f9e0f3dd7a37b6f3d136a444cd6a0d
branch: main
author: Lukas Geiger <[email protected]>
committer: markshannon <[email protected]>
date: 2026-09-30T14:32:18+01:00
summary:
gh-158072: Fix JIT crash with omitted default arguments (GH-158128)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-09-24-18-44-40.gh-issue-158072.mHveWw.rst
M Lib/test/test_capi/test_opt.py
M Python/optimizer_symbols.c
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 31440c8e609213..5f2843e56f46ad 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -1812,6 +1812,37 @@ def testfunc(n):
# __init__ resolution allows promotion of range to constant
self.assertNotIn("_LOAD_GLOBAL_BUILTINS", uops)
+ # See https://github.com/python/cpython/issues/158072
+ def test_init_with_default_argument(self):
+ script_helper.assert_python_ok("-c", textwrap.dedent(f"""\
+ sentinel = object()
+
+ class WithDefault:
+ def __init__(self, value=sentinel):
+ if value is not sentinel:
+ pass
+
+ for _ in range({TIER2_THRESHOLD * 3}):
+ WithDefault()
+ """), PYTHON_JIT="1")
+
+ def test_init_with_changed_code_argcount(self):
+ class C:
+ def __init__(self, value):
+ self.value = False
+
+ def varargs_init(self, *args):
+ self.value = isinstance(args, tuple)
+
+ def testfunc(n):
+ for _ in range(n):
+ result = C(42).value
+ return result
+
+ self.assertFalse(testfunc(100))
+ C.__init__.__code__ = C.varargs_init.__code__
+ self.assertTrue(testfunc(TIER2_THRESHOLD * 3))
+
def test_init_guards_removed(self):
class MyPoint:
def __init__(self, x, y):
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-24-18-44-40.gh-issue-158072.mHveWw.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-24-18-44-40.gh-issue-158072.mHveWw.rst
new file mode 100644
index 00000000000000..faff8fff4aec5c
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-24-18-44-40.gh-issue-158072.mHveWw.rst
@@ -0,0 +1 @@
+Fix a JIT crash when a class is instantiated with their default arguments
diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c
index 79f81482d247e3..3147e9c5065edd 100644
--- a/Python/optimizer_symbols.c
+++ b/Python/optimizer_symbols.c
@@ -1388,17 +1388,27 @@ _Py_uop_frame_new(
}
// Initialize with the initial state of all local variables
+ arg_len = Py_MIN(arg_len, co->co_argcount);
for (int i = 0; i < arg_len; i++) {
frame->locals[i] = PyJitRef_RemoveUnique(args[i]);
}
- // If the args are known, then it's safe to just initialize
- // every other non-set local to null symbol.
- bool default_null = args != NULL;
+ // When args is available, missing parameters get defaults or
*args/**kwargs.
+ // Other locals start as NULL. When args is NULL, treat all locals as
unknown.
+ int parameter_count = co->co_argcount + co->co_kwonlyargcount;
+ parameter_count += (co->co_flags & CO_VARARGS) != 0;
+ parameter_count += (co->co_flags & CO_VARKEYWORDS) != 0;
for (int i = arg_len; i < co->co_nlocalsplus; i++) {
- JitOptRef local = default_null ? _Py_uop_sym_new_null(ctx) :
_Py_uop_sym_new_unknown(ctx);
- frame->locals[i] = local;
+ if (args == NULL) {
+ frame->locals[i] = _Py_uop_sym_new_unknown(ctx);
+ }
+ else if (i < parameter_count) {
+ frame->locals[i] = _Py_uop_sym_new_not_null(ctx);
+ }
+ else {
+ frame->locals[i] = _Py_uop_sym_new_null(ctx);
+ }
}
frame->callable = _Py_uop_sym_new_not_null(ctx);
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]