Skip to content

Commit 6ed227b

Browse files
cmaloneyclaude
authored andcommitted
gh-159184: Do not intern NaN constants on the free-threaded build (GH-161276)
compare_constants() matches floats by bits, but hash_const() uses the object hash, which for a NaN depends on its address, so each NaN loaded by marshal.loads() added an immortal entry to the constants table. Hashing the bits would merge NaNs and shrink frozensets, so leave them out of the table. (cherry picked from commit 1db5966) Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 6802496 commit 6ed227b

2 files changed

Lines changed: 12 additions & 2 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a leak of NaN constants in code objects created by :func:`marshal.loads`
2+
on the free-threaded build.

‎Objects/codeobject.c‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,16 @@ should_immortalize_constant(PyObject *v)
173173
_Py_IsImmortal(slice->stop) &&
174174
_Py_IsImmortal(slice->step));
175175
}
176-
return (PyLong_CheckExact(v) || PyFloat_CheckExact(v) ||
177-
PyComplex_CheckExact(v) || PyBytes_CheckExact(v));
176+
// The hash of a NaN depends on its address, so the table would keep every
177+
// NaN object it gets (and merging them would shrink frozensets).
178+
if (PyFloat_CheckExact(v)) {
179+
return !isnan(PyFloat_AS_DOUBLE(v));
180+
}
181+
if (PyComplex_CheckExact(v)) {
182+
Py_complex c = ((PyComplexObject *)v)->cval;
183+
return !isnan(c.real) && !isnan(c.imag);
184+
}
185+
return PyLong_CheckExact(v) || PyBytes_CheckExact(v);
178186
}
179187
#endif
180188

0 commit comments

Comments
 (0)