diff --git a/graalpython/com.oracle.graal.python.cext/src/floatobject.c b/graalpython/com.oracle.graal.python.cext/src/floatobject.c index a620a08f61..48c39b8c81 100644 --- a/graalpython/com.oracle.graal.python.cext/src/floatobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/floatobject.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, 2025, Oracle and/or its affiliates. +/* Copyright (c) 2018, 2026, Oracle and/or its affiliates. * Copyright (C) 1996-2017 Python Software Foundation * * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -2689,7 +2689,6 @@ GraalPyPrivate_Float_SubtypeNew(PyTypeObject *type, double x) { PyObject* newobj = type->tp_alloc(type, 0); if (newobj == NULL) { - Py_DECREF(newobj); return NULL; } ((PyFloatObject *)newobj)->ob_fval = x; diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_float.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_float.py index 6f4b3cb978..9cfa28cb31 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_float.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_float.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -245,6 +245,25 @@ class ManagedSubclass(NativeFloatSubclass): f = ManagedSubclass(1.0) assert is_native_object(f) + def test_alloc_failure(self): + """A native float subtype must propagate tp_alloc failure instead of crashing on NULL.""" + FailingAllocFloatSubclass = CPyExtType( + 'FailingAllocFloatSubclass', + r''' + static PyObject* fail_alloc(PyTypeObject *type, Py_ssize_t nitems) { + PyErr_NoMemory(); + return NULL; + } + ''', + struct_base='PyFloatObject base;', + tp_base='&PyFloat_Type', + tp_new='0', + tp_alloc='fail_alloc', + tp_free='0', + ) + with self.assertRaises(MemoryError): + FailingAllocFloatSubclass(1.0) + def test_methods(self): f = NativeFloatSubclass(1.1) zero = NativeFloatSubclass(0.0) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java index 70bce259a9..995a9ce131 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java @@ -205,15 +205,18 @@ public abstract static class FloatSubtypeNew extends Node { static Object doGeneric(Object object, double arg, @Bind Node inliningTarget, @Cached PythonToNativeNode toNativeNode, - @Cached NativeToPythonTransferNode toJavaNode) { + @Cached NativeToPythonTransferNode toJavaNode, + @Cached PyObjectCheckFunctionResultNode checkFunctionResultNode) { assert TypeNodes.NeedsNativeAllocationNode.executeUncached(object); NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_FLOAT_SUBTYPE_NEW); + long result; try { - long result = ExternalFunctionInvoker.invokeFLOAT_SUBTYPE_NEW(callable.getAddress(), toNativeNode.executeLong(object), arg); - return toJavaNode.execute(result); + result = ExternalFunctionInvoker.invokeFLOAT_SUBTYPE_NEW(callable.getAddress(), toNativeNode.executeLong(object), arg); } catch (Throwable e) { throw CompilerDirectives.shouldNotReachHere(e); } + return checkFunctionResultNode.execute(PythonContext.get(inliningTarget), NativeCAPISymbol.FUN_FLOAT_SUBTYPE_NEW.getTsName(), + toJavaNode.execute(result)); } }