Skip to content

Commit 7c231b6

Browse files
committed
[mypyc] feat: make Final values immortal on py3.12+
1 parent feeb3f0 commit 7c231b6

File tree

5 files changed

+23
-4
lines changed

5 files changed

+23
-4
lines changed

mypyc/irbuild/builder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,9 @@ def debug_print(self, toprint: str | Value) -> None:
428428
def set_immortal_if_free_threaded(self, v: Value, line: int) -> None:
429429
"""Make an object immortal on free-threaded builds (to avoid contention)."""
430430
self.builder.set_immortal_if_free_threaded(v, line)
431+
432+
def set_immortal_if_py312plus(self, v: Value, line: int) -> None:
433+
self.builder.set_immortal_if_py312plus(v, line)
431434

432435
# Helpers for IR building
433436

@@ -572,6 +575,7 @@ def init_final_static(
572575
coerced = self.coerce(rvalue_reg, type_override or self.node_type(lvalue), lvalue.line)
573576
self.final_names.append((name, coerced.type))
574577
self.add(InitStatic(coerced, name, self.module_name))
578+
self.set_immortal_if_py312plus(coerced, lvalue.line)
575579

576580
def load_final_static(
577581
self, fullname: str, typ: RType, line: int, error_name: str | None = None

mypyc/irbuild/classdef.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def finalize(self, ir: ClassIR) -> None:
263263
non_ext_class = load_decorated_class(self.builder, self.cdef, non_ext_class)
264264

265265
# Try to avoid contention when using free threading.
266-
self.builder.set_immortal_if_free_threaded(non_ext_class, self.cdef.line)
266+
self.builder.builder.set_immortal_if_py312plus(non_ext_class, self.cdef.line)
267267

268268
# Save the decorated class
269269
self.builder.add(

mypyc/irbuild/ll_builder.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,6 +2491,11 @@ def set_immortal_if_free_threaded(self, v: Value, line: int) -> None:
24912491
"""Make an object immortal on free-threaded builds (to avoid contention)."""
24922492
if IS_FREE_THREADED and sys.version_info >= (3, 14):
24932493
self.primitive_op(set_immortal_op, [v], line)
2494+
2495+
def set_immortal_if_py312plus(self, v: Value, line: int) -> None:
2496+
"""Emit IR to mark an object as immortal in Python 3.12+ (calls CPy_SetImmortal)."""
2497+
if sys.version_info >= (3, 12):
2498+
self.primitive_op(set_immortal_op, [v], line)
24942499

24952500
# Internal helpers
24962501

mypyc/lib-rt/misc_ops.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1129,12 +1129,22 @@ void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_valu
11291129

11301130
#endif
11311131

1132-
#if CPY_3_14_FEATURES
1132+
#if CPY_3_12_FEATURES
11331133

11341134
#include "internal/pycore_object.h"
11351135

1136+
#if !CPY_3_14_FEATURES
1137+
// The immortal refcount value is -1 (see PEP 683 and CPython source)
1138+
// This is safe for statically allocated objects and matches CPython's logic.
1139+
#define IMMORTAL_REFCNT ((Py_ssize_t)-1)
1140+
#endif
1141+
11361142
void CPy_SetImmortal(PyObject *obj) {
1143+
#if CPY_3_14_FEATURES
11371144
_Py_SetImmortal(obj);
1145+
#elif CPY_3_12_FEATURES
1146+
Py_SET_REFCNT(obj, IMMORTAL_REFCNT);
1147+
#endif
11381148
}
11391149

11401150
#endif

mypyc/primitives/misc_ops.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,9 @@
324324
# threading, since this eliminates contention from concurrent reference count
325325
# updates.
326326
#
327-
# Needs at least Python 3.14.
327+
# Available for Python 3.12+ (uses public API in 3.14+, internal logic in 3.12/3.13).
328328
set_immortal_op = custom_primitive_op(
329-
name="set_immmortal",
329+
name="set_immortal",
330330
c_function_name="CPy_SetImmortal",
331331
arg_types=[object_rprimitive],
332332
return_type=void_rtype,

0 commit comments

Comments
 (0)