diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 1d2c105ac047e18..38599cc3afb9c12 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -2229,6 +2229,18 @@ def __getitem__(self, index): self.assertComplexesAreIdentical(sum([1.0, complex(1, -0.0)]), complex(2, -0.0)) + def test_sum_float_subclass(self): + # gh-151060: the C fast-sum loop must not free a float subclass with + # the exact-float deallocator. A generator keeps each item's only + # reference in sum(), so the specialized DECREF runs the deallocator. + class F(float): + count = 0 + def __del__(self): + F.count += 1 + + self.assertEqual(sum((F(i) for i in range(5)), 1j), complex(10, 1)) + self.assertEqual(F.count, 5) + @requires_IEEE_754 @skip_if_double_rounding @support.cpython_only # Other implementations may choose a different algorithm diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-07-22-03-20.gh-issue-151060.tdexEE.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-07-22-03-20.gh-issue-151060.tdexEE.rst new file mode 100644 index 000000000000000..1efde8a536c0939 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-07-22-03-20.gh-issue-151060.tdexEE.rst @@ -0,0 +1,2 @@ +Fix a crash or memory corruption in :func:`sum` when summing a +:class:`float` subclass instance into a complex running total. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index d5129bf6a5a6bc0..fbdd637a3fcb78d 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -3046,7 +3046,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) return NULL; } } - if (PyFloat_Check(item)) { + if (PyFloat_CheckExact(item)) { double value = PyFloat_AS_DOUBLE(item); re_sum = cs_add(re_sum, value); _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);