diff --git a/Lib/test/test_tuple.py b/Lib/test/test_tuple.py index e533392b8cae94..6564fa04f1bcbe 100644 --- a/Lib/test/test_tuple.py +++ b/Lib/test/test_tuple.py @@ -319,10 +319,15 @@ def test_track_literals(self): self._not_tracked_instantly((1,)) self._not_tracked_instantly((1, 2)) self._not_tracked_instantly((1, 2, "a")) - self._not_tracked_instantly((1, 2) * 5) self._not_tracked_instantly((12, 10**10, 'a_' * 100)) self._not_tracked_instantly((object(),)) + # Test for _PyTuple_Concat + self._not_tracked_instantly((1, 2) + (2, 3)) + + # Test for _PyTuple_Repeat + self._not_tracked_instantly((1, 2) * 5) + self._not_tracked(((1, x), y, (2, 3))) self._not_tracked((1, 2, (None, True, False, ()), int)) self._not_tracked((object(), ())) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-26-19-28-49.gh-issue-155728.LJIPeZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-26-19-28-49.gh-issue-155728.LJIPeZ.rst new file mode 100644 index 00000000000000..5ef398285f6f37 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-26-19-28-49.gh-issue-155728.LJIPeZ.rst @@ -0,0 +1,2 @@ +The flowgraph optimizer now untracks tuples from the GC when possible. Patch +by Sergey Miryanov. diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index bb5e18cb790acf..bac836457422df 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -593,7 +593,10 @@ _PyTuple_Concat(PyObject *aa, PyObject *bb) dest[i] = Py_NewRef(v); } - _PyObject_GC_TRACK(np); + if (_PyObject_GC_IS_TRACKED(aa) || _PyObject_GC_IS_TRACKED(bb)) + { + _PyObject_GC_TRACK(np); + } return (PyObject *)np; } @@ -642,7 +645,10 @@ _PyTuple_Repeat(PyObject *self, Py_ssize_t n) _Py_memory_repeat((char *)np->ob_item, sizeof(PyObject *)*output_size, sizeof(PyObject *)*input_size); } - _PyObject_GC_TRACK(np); + if (_PyObject_GC_IS_TRACKED(self)) + { + _PyObject_GC_TRACK(np); + } return (PyObject *) np; } diff --git a/Python/flowgraph.c b/Python/flowgraph.c index a5138d1a1fa284..be56dd0bce14f6 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -12,6 +12,7 @@ #include "pycore_opcode_metadata.h" // OPCODE_HAS_ARG, etc #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_stackref.h" // PyStackRef_AsPyObjectBorrow() +#include "pycore_tuple.h" // _PyTuple_MaybeUntrack() #include @@ -1563,6 +1564,7 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, } PyTuple_SET_ITEM(const_tuple, i, element); } + _PyTuple_MaybeUntrack(const_tuple); nop_out(const_instrs, seq_size); return instr_make_load_const(instr, const_tuple, consts, const_cache, consts_index); @@ -1650,6 +1652,7 @@ fold_constant_seq_into_load_const(basicblock *bb, int i, } nop_out(&instr, 1); } + _PyTuple_MaybeUntrack(newconst); assert(consts_found == 0); if (build_op == BUILD_SET) { @@ -1737,6 +1740,7 @@ optimize_lists_and_sets(basicblock *bb, int i, int nextop, } PyTuple_SET_ITEM(const_result, i, element); } + _PyTuple_MaybeUntrack(const_result); if (instr->i_opcode == BUILD_SET) { PyObject *frozenset = PyFrozenSet_New(const_result);