Skip to content

Commit d9232e5

Browse files
committed
Fix: __ipow__ is always called as binary
1 parent 0b98981 commit d9232e5

File tree

2 files changed

+44
-13
lines changed

2 files changed

+44
-13
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/hpy/GraalHPyContextFunctions.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,6 @@ static final class CallArithmeticRootNode extends PRootNode {
392392
private static final Signature SIGNATURE_UNARY = new Signature(1, false, -1, false, new String[]{"$self"}, null);
393393
private static final Signature SIGNATURE_BINARY = new Signature(2, false, -1, false, new String[]{"$self", "other"}, null);
394394
private static final Signature SIGNATURE_TERNARY = new Signature(3, false, -1, false, new String[]{"x", "y", "z"}, null);
395-
private static final Signature SIGNATURE_INPLACE = new Signature(3, false, -1, false, new String[]{"x", "y"}, null);
396395

397396
@Child private LookupAndCallUnaryNode callUnaryNode;
398397
@Child private LookupAndCallBinaryNode callBinaryNode;
@@ -422,9 +421,7 @@ public Signature getSignature() {
422421
return SIGNATURE_UNARY;
423422
} else if (binaryOperator != null) {
424423
return SIGNATURE_BINARY;
425-
} else if (inplaceOperator != null) {
426-
return SIGNATURE_INPLACE;
427-
} else if (ternaryOperator != null) {
424+
} else if (inplaceOperator != null || ternaryOperator != null) {
428425
return SIGNATURE_TERNARY;
429426
} else {
430427
throw CompilerDirectives.shouldNotReachHere();
@@ -455,7 +452,13 @@ public Object execute(VirtualFrame frame) {
455452
} else if (binaryOperator != null) {
456453
return callBinaryNode.executeObject(frame, PArguments.getArgument(frame, 0), PArguments.getArgument(frame, 1));
457454
} else if (inplaceOperator != null) {
458-
return callInplaceNode.execute(frame, PArguments.getArgument(frame, 0), PArguments.getArgument(frame, 1));
455+
// most of the in-place operators are binary but there can also be ternary
456+
if (PArguments.getUserArgumentLength(frame) == 2) {
457+
return callInplaceNode.execute(frame, PArguments.getArgument(frame, 0), PArguments.getArgument(frame, 1));
458+
} else if (PArguments.getUserArgumentLength(frame) == 3) {
459+
return callInplaceNode.executeTernary(frame, PArguments.getArgument(frame, 0), PArguments.getArgument(frame, 1), PArguments.getArgument(frame, 2));
460+
}
461+
throw CompilerDirectives.shouldNotReachHere();
459462
} else if (ternaryOperator != null) {
460463
return callTernaryNode.execute(frame, PArguments.getArgument(frame, 0), PArguments.getArgument(frame, 1), PArguments.getArgument(frame, 2));
461464
} else {
@@ -531,7 +534,13 @@ Object execute(Object[] arguments,
531534
@Cached HPyAsPythonObjectNode asPythonObjectNode,
532535
@Cached HPyAsHandleNode asHandleNode,
533536
@Cached GenericInvokeNode invokeNode,
534-
@Cached TransformExceptionToNativeNode transformExceptionToNativeNode) {
537+
@Cached TransformExceptionToNativeNode transformExceptionToNativeNode) throws ArityException {
538+
539+
// We need to do argument checking at this position because our helper root node that
540+
// just dispatches to the appropriate 'LookupAndCallXXXNode' won't do any arguemnt
541+
// checking. So it would just crash if there are too few arguments or just ignore if
542+
// there are too many.
543+
checkArguments(arguments);
535544

536545
GraalHPyContext context = asContextNode.execute(arguments[0]);
537546
Object[] pythonArguments = PArguments.create(arguments.length - 1);
@@ -549,6 +558,29 @@ Object execute(Object[] arguments,
549558
}
550559
}
551560

561+
private void checkArguments(Object[] arguments) throws ArityException {
562+
// note: we always add 1 for the context
563+
int min;
564+
int max;
565+
if (unaryOperator != null) {
566+
min = max = 2;
567+
} else if (binaryOperator != null) {
568+
min = max = 3;
569+
} else if (inplaceOperator != null) {
570+
min = 3;
571+
max = 4;
572+
} else if (ternaryOperator != null) {
573+
min = max = 4;
574+
} else {
575+
throw CompilerDirectives.shouldNotReachHere();
576+
}
577+
if (min > arguments.length || max < arguments.length) {
578+
CompilerDirectives.transferToInterpreterAndInvalidate();
579+
int expected = min > arguments.length ? min : max;
580+
throw ArityException.create(expected, arguments.length);
581+
}
582+
}
583+
552584
private RootCallTarget getCallTarget(GraalHPyArithmetic receiver, PythonLanguage language) {
553585
RootCallTarget callTarget = receiver.callTarget;
554586
if (callTarget == null) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/expression/LookupAndCallInplaceNode.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import com.oracle.graal.python.builtins.objects.PNone;
4444
import com.oracle.graal.python.builtins.objects.PNotImplemented;
45+
import com.oracle.graal.python.nodes.PGuards;
4546
import com.oracle.graal.python.nodes.PNodeWithContext;
4647
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
4748
import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
@@ -173,21 +174,19 @@ Object doInplaceOnly(VirtualFrame frame, Object left, Object right, Object z,
173174
@Specialization(guards = "hasNonInplaceOperator()")
174175
Object doBinary(VirtualFrame frame, Object left, Object right, Object z,
175176
@Cached("create(inplaceOpName)") LookupInheritedAttributeNode getattrInplace) {
176-
Object result = PNotImplemented.NOT_IMPLEMENTED;
177+
Object result;
177178
Object inplaceCallable = getattrInplace.execute(left);
178-
boolean isBinary = z == PNone.NO_VALUE;
179179
if (inplaceCallable != PNone.NO_VALUE) {
180-
if (isBinary) {
181-
result = ensureBinaryCallNode().executeObject(frame, inplaceCallable, left, right);
182-
} else {
183-
result = ensureTernaryCallNode().execute(frame, inplaceCallable, left, right, z);
184-
}
180+
// nb.: The only ternary in-place operator is '__ipow__' but according to 'typeobject.c:
181+
// slot_nb_inplace_power', this is always called as binary.
182+
result = ensureBinaryCallNode().executeObject(frame, inplaceCallable, left, right);
185183
if (result != PNotImplemented.NOT_IMPLEMENTED) {
186184
return result;
187185
}
188186
}
189187

190188
// try non-inplace variant
189+
boolean isBinary = PGuards.isPNone(z);
191190
if (isBinary) {
192191
result = ensureLookupAndCallBinaryNode().executeObject(frame, left, right);
193192
} else {

0 commit comments

Comments
 (0)