From e8b322b2f1644be07549b02f5deaefc3462893bc Mon Sep 17 00:00:00 2001 From: Jeongseop Lim Date: Tue, 20 Oct 2026 02:28:24 +0900 Subject: [PATCH] Check both complex() arguments before converting either complexFromComplexObject, the specialization taken when imag is neither a complex nor a string, called imag's __float__ before it looked at real, so an exception from that conversion replaced the first-argument TypeError and complex(Real(), Imag()) converted imag first. CPython's complex_new_impl checks the type of real, then of imag, and only then converts real followed by imag. The specialization now does the same; the (Object, long/double/PInt) specializations already check real first. Signed-off-by: Jeongseop Lim --- .../src/tests/test_complex.py | 14 ++++++++++++ .../objects/complex/ComplexBuiltins.java | 22 ++++++++----------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py b/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py index e1b6ea9ec9..096e2256ee 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py @@ -304,6 +304,20 @@ class SubComplex(complex): assert result == 2 + 3j assert type(result) is complex + +def test_create_checks_arguments_before_converting(): + class RaisingReal: + def __float__(self): + raise KeyError + + class RaisingImag: + def __float__(self): + raise RuntimeError + + assert_raises(TypeError, complex, {}, RaisingImag()) + assert_raises(TypeError, complex, RaisingReal(), {}) + assert_raises(KeyError, complex, RaisingReal(), RaisingImag()) + class ComplexTest(unittest.TestCase): def assertAlmostEqual(self, a, b): diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java index 6dcc4631a6..8a54ebd963 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java @@ -618,21 +618,17 @@ Object complexFromComplexObject(VirtualFrame frame, Object cls, Object one, Obje @Cached.Shared("tryComplex") @Cached TryComplexSpecialMethodNode tryComplexNode, @Cached.Shared @Cached PRaiseNode raiseNode) { PComplex oneValue = getComplexNumberFromObject(frame, one, inliningTarget, isComplexType, tryComplexNode); - if (canBeDoubleNode.execute(inliningTarget, two)) { - double twoValue = asDoubleNode.execute(frame, inliningTarget, two); - if (oneValue == null) { - if (one == PNone.NO_VALUE) { - return createComplexNode.execute(inliningTarget, cls, 0, twoValue); - } else if (canBeDoubleNode.execute(inliningTarget, one)) { - return createComplexNode.execute(inliningTarget, cls, asDoubleNode.execute(frame, inliningTarget, one), twoValue); - } else { - throw raiseFirstArgError(one, inliningTarget, raiseNode); - } - } - return createComplexNode.execute(inliningTarget, cls, oneValue.getReal(), oneValue.getImag() + twoValue); - } else { + if (oneValue == null && one != PNone.NO_VALUE && !canBeDoubleNode.execute(inliningTarget, one)) { + throw raiseFirstArgError(one, inliningTarget, raiseNode); + } + if (!canBeDoubleNode.execute(inliningTarget, two)) { throw raiseSecondArgError(two, inliningTarget, raiseNode); } + if (oneValue == null) { + double oneDouble = one == PNone.NO_VALUE ? 0 : asDoubleNode.execute(frame, inliningTarget, one); + return createComplexNode.execute(inliningTarget, cls, oneDouble, asDoubleNode.execute(frame, inliningTarget, two)); + } + return createComplexNode.execute(inliningTarget, cls, oneValue.getReal(), oneValue.getImag() + asDoubleNode.execute(frame, inliningTarget, two)); } @Specialization