Skip to content

gh-150868: Fix sum() deallocating a float subclass as an exact float - #151063

Open
eendebakpt wants to merge 2 commits into
python:mainfrom
eendebakpt:fix-sum-float-subclass-dealloc
Open

gh-150868: Fix sum() deallocating a float subclass as an exact float#151063
eendebakpt wants to merge 2 commits into
python:mainfrom
eendebakpt:fix-sum-float-subclass-dealloc

Conversation

@eendebakpt

@eendebakpt eendebakpt commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

…float

The complex-total fast path in builtin_sum_impl() guarded with
PyFloat_Check() (which accepts subclasses) but freed the item with
_Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc), which assumes an
exact float. A float subclass instance reaching refcount 0 in that loop
aborts on an assertion in debug builds, and in release builds skips the
subclass tp_dealloc (type refcount leak, __del__ never runs, subclass
object pushed onto the float freelist).

Use PyFloat_CheckExact() instead, matching the float-total branch, so
subclass instances fall through to the generic PyNumber_Add() path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@StanFromIreland StanFromIreland changed the title gh-151060: Fix sum() deallocating a float subclass as an exact float gh-150868: Fix sum() deallocating a float subclass as an exact float Jun 7, 2026
@eendebakpt

Copy link
Copy Markdown
Contributor Author

There was already a PR opened earlier, lets give that a chance to be merged.

@skirpichev

Copy link
Copy Markdown
Member

I like this pr, it has a test.

@eendebakpt, could you extend this fix with this (and add tests too):
#150868 (comment)
?

…) loops

Per review: use PyLong_CheckExact with the specialized exact-int
deallocator in the float and complex fast-sum loops, so int subclasses
take the generic path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@eendebakpt

Copy link
Copy Markdown
Contributor Author

@skirpichev I updated the branch with your suggestion. I think this will need backports.

@skirpichev

Copy link
Copy Markdown
Member

CC @picnixz

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread Python/bltinmodule.c
_Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);
continue;
}
if (PyLong_Check(item)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change really necessary? we lose a fast past for int subclasses here or am I missing something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is needed in combination with the _Py_DECREF_SPECIALIZED that was added. I think we can keep the PyLong_Check and use the normal decref. Not sure either approach is much better for performance.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but what I meant is that we do not need to have a _Py_DECREF_SPECIALIZED for the int case right? I'm not even sure why we need the float case. Was there a real need for exact floats or was it an historical behavior? I don't remember anymore :(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The specialized DECREF was added by commit da6c785, when sum() already checked for PyFloat_CheckExact():

-                Py_DECREF(item);
+                _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);

Supporting int and float subclasses require to decide how to deal with overriden __index__() and __float__() methods.

class StrangeInt(int):
    def __index__(self):
        return 42.0

a = 1
b = StrangeInt(2)
print(sum([a, b]))

class StrangeFloat(float):
    def __float__(self):
        return 42.0

a = 1.0
b = StrangeFloat(2.0)
print(sum([a, b]))

On Python 3.16, this code displays 3 and 3.0. So it seems like __index__() and __float__() methods are not called in these cases.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change really necessary? we lose a fast past for int subclasses here or am I missing something?

I think that specialization for subclasses was added not intentionally. In other cases we use strict checks.

WRT performance, I doubt we will have too much from specialized decrefs. But lets keep this to emphasize that we deal with base classes only.

Supporting int and float subclasses require to decide how to deal with overriden __index__() and __float__() methods.

All examples, where it matters, looks broken for me (like above).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok we can keep the strict checks only and wonder later about what to do with the checks. AFAIR the PyLong_AsDouble doesn'z care about conversion functions but it also doesn't check for exact type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting merge needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants