Skip to content

Commit 1db5966

Browse files
cmaloneyclaude
andauthored
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. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 064180b commit 1db5966

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)