Skip to content

Remove redundant decref from _PyLong_Negate #143050

Description

@eendebakpt

Feature 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

Activity

  1. serhiy-storchaka commented on Dec 23, 2025

    @serhiy-storchaka
    Member

    Even if they are immortal, I prefer to keep incref/decref balanced. This saves time when you read code.

  2. sergey-miryanov commented on Dec 23, 2025

    @sergey-miryanov
    Contributor

    Maybe it is worth to replace Py_DECREF with something like Py_DECREF_IMMORTAL?

  3. eendebakpt commented on Dec 23, 2025

    @eendebakpt
    ContributorAuthor

    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

  4. added a commit that references this issue on Mar 10, 2026
  5. colesbury commented on Mar 10, 2026

    @colesbury
    Contributor

    Thanks

  6. added a commit that references this issue on Mar 12, 2026
  7. StanFromIreland commented on Mar 12, 2026

    @StanFromIreland
    Member

    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)
    
  8. eendebakpt commented on Mar 12, 2026

    @eendebakpt
    ContributorAuthor

    Thanks for reporting, I will have a look at the reproducer tomorrow.

  9. added a commit that references this issue on Mar 13, 2026
  10. colesbury commented on Mar 13, 2026

    @colesbury
    Contributor

    I stuck this into Claude and I think it (mostly) got it right:

    1. int('-' + '0' * 7000, 10) -> PyLong_FromString -> _pylong.int_from_string, which returns 0
    2. We then call _PyLong_FlipSign on it, which corrupts 0

    So it was a pre-existing bug in PyLong_FromString.

  11. self-assigned this
    on Mar 13, 2026
  12. 13 remaining items

  13. skirpichev commented on Mar 31, 2026

    @skirpichev
    Member

    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).

  14. added 3 commits that reference this issue on Apr 1, 2026
  15. vstinner commented on Apr 1, 2026

    @vstinner
    Member

    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.

  16. skirpichev commented on Apr 1, 2026

    @skirpichev
    Member

    Even if they are immortal, I prefer to keep incref/decref balanced.

    We can use _Py_DECREF_INT here, no?

  17. added a commit that references this issue on Apr 1, 2026
  18. added 2 commits that reference this issue on Apr 16, 2026
  19. added 4 commits that reference this issue on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions