GeezerVison is showing github.com — Remove redundant decref from _PyLong_Negate · Issue #143050 · python/cpython
Remove redundant decref from _PyLong_Negate #143050DescriptionFeature or enhancement
Proposal:
In _PyLong_Negate there is a fast path for unique referenced longs. The slow path assumes the argument is a small int. We can add an assert for this condition, and remove the final decref (small ints are immortal).
Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
No response
Linked PRs
Activityserhiy-storchaka commented on Dec 23, 2025 Even if they are immortal, I prefer to keep incref/decref balanced. This saves time when you read code. sergey-miryanov commented on Dec 23, 2025 Maybe it is worth to replace Py_DECREF with something like Py_DECREF_IMMORTAL? eendebakpt commented on Dec 23, 2025 ContributorAuthor More actions
Maybe it is worth to replace Py_DECREF with something like Py_DECREF_IMMORTAL?
I would be in favor of that. There is a related discussion here #124074 Reacted by Sergey Miryanov colesbury commented on Mar 10, 2026 StanFromIreland commented on Mar 12, 2026 This introduced a regression that was caught by our OSS Fuzzer (https://oss-fuzz.com/testcase-detail/6342648498814976).
$ ./python -c "
import sys
sys.set_int_max_str_digits(10000)
while True:
try:
int('-' + '0' * 7000, 10)
except:
pass
"
python: Objects/longobject.c:371: _PyLong_Negate: Assertion `_long_is_small_int((PyObject *)x)' failed.
Aborted (core dumped)
full reproducer
To reproduce, download the testcase: https://drive.google.com/file/d/1Ain_DfzbodyKU_YiwtZPaT4ViAOssvOt/view?usp=sharing
And run it to trigger the assert, note that it requires a few iterations:
$ ./python -c "
import sys
sys.set_int_max_str_digits(10000)
with open('/tmp/inttestcase','rb') as f:
data = f.read().decode('utf-8')
while True:
try:
int(data, 10)
except:
pass
"
python: Objects/longobject.c:371: _PyLong_Negate: Assertion `_long_is_small_int((PyObject *)x)' failed.
Aborted (core dumped)
eendebakpt commented on Mar 12, 2026 ContributorAuthor More actions Thanks for reporting, I will have a look at the reproducer tomorrow. colesbury commented on Mar 13, 2026 I stuck this into Claude and I think it (mostly) got it right:
int('-' + '0' * 7000, 10) -> PyLong_FromString -> _pylong.int_from_string, which returns 0
- We then call
_PyLong_FlipSign on it, which corrupts 0
So it was a pre-existing bug in PyLong_FromString. skirpichev commented on Mar 31, 2026
I don't think that avoiding one Py_DECREF() in _PyLong_Negate() is worth it.
Why not do it?
I think that original PR was correct and now @eendebakpt can restore it with small adaptation (for a new helper). vstinner commented on Apr 1, 2026
Why not do it?
I will quote @serhiy-storchaka:
Even if they are immortal, I prefer to keep incref/decref balanced. This saves time when you read code.
skirpichev commented on Apr 1, 2026
Even if they are immortal, I prefer to keep incref/decref balanced.
We can use _Py_DECREF_INT here, no?
MetadataMetadataStatusShow more project fields
DevelopmentNo branches or pull requestsIssue actions
|