# HG changeset patch
# User Kai T. Ohlhus <k.ohlhus@gmail.com>
# Date 1537963832 -7200
#      Wed Sep 26 14:10:32 2018 +0200
# Branch stable
# Node ID b89b63b2bc9497909e65003154f7378954b23456
# Parent  49734a75561f14c23ed23a13cf6b47ef20d6cc40
Regard the precedence of binary operators for old style classes (bug #42735).

* libinterp/operators/op-class.cc (DEF_CLASS_BINOP): Refactor the precedence
determination as follows:

1.   If exactly one of the arguments is a user defined class object, then
     the function of that operand's class is invoked.
2.   If both arguments are user defined class objects, then
2.1  The superior class function is invoked.
2.2  The leftmost class function is invoked if both classes are the same
     or their precedence is not defined by superiorto/inferiorto.

* test/classes/classes.tst:  Add test cases for overloaded binary operator "+".

* test/classes/@CPredecendeTester1, test/classes/@CPredecendeTester2,
test/classes/@CPredecendeTester3:  Extend classes for new test cases with a
double conversion function and an overloaded plus function.

diff -r 49734a75561f -r b89b63b2bc94 libinterp/operators/op-class.cc
--- a/libinterp/operators/op-class.cc	Thu Sep 13 11:04:57 2018 -0700
+++ b/libinterp/operators/op-class.cc	Wed Sep 26 14:10:32 2018 +0200
@@ -74,20 +74,32 @@ DEF_CLASS_UNOP (uminus)
 DEF_CLASS_UNOP (transpose)
 DEF_CLASS_UNOP (ctranspose)
 
-// FIXME: we need to handle precedence in the binop function.
+// The precedence of the oct_binop_*-functions is as follows:
+//
+// 1.   If exactly one of the arguments is a user defined class object, then
+//      the function of that operand's class is invoked.
+//
+// 2.   If both arguments are user defined class objects, then
+// 2.1  The superior class function is invoked.
+// 2.2  The leftmost class function is invoked if both classes are the same
+//      or their precedence is not defined by superiorto/inferiorto.
 
-#define DEF_CLASS_BINOP(name) \
+#define DEF_CLASS_BINOP(name)                                           \
   static octave_value                                                   \
   oct_binop_ ## name (const octave_value& a1, const octave_value& a2)   \
   {                                                                     \
     octave_value retval;                                                \
                                                                         \
-    std::string dispatch_type                                           \
-      = (a1.isobject () ? a1.class_name () : a2.class_name ());         \
-                                                                        \
     octave::symbol_table& symtab                                        \
       = octave::__get_symbol_table__ ("oct_unop_" #name);               \
                                                                         \
+    std::string dispatch_type = a1.class_name ();                       \
+                                                                        \
+    if (! a1.isobject ()                                                \
+        || (a1.isobject () && a2.isobject ()                            \
+            && symtab.is_superiorto (a2.class_name (), dispatch_type))) \
+      dispatch_type = a2.class_name ();                                 \
+                                                                        \
     octave_value meth = symtab.find_method (#name, dispatch_type);      \
                                                                         \
     if (meth.is_undefined ())                                           \
diff -r 49734a75561f -r b89b63b2bc94 test/classes/@CPrecedenceTester1/double.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/classes/@CPrecedenceTester1/double.m	Wed Sep 26 14:10:32 2018 +0200
@@ -0,0 +1,5 @@
+function x = double (snk)
+
+  x = snk.useless_data;
+
+end
diff -r 49734a75561f -r b89b63b2bc94 test/classes/@CPrecedenceTester1/plus.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/classes/@CPrecedenceTester1/plus.m	Wed Sep 26 14:10:32 2018 +0200
@@ -0,0 +1,5 @@
+function s = plus (s1, s2)
+
+  s = 'CPrecedenceTester1';
+
+end
diff -r 49734a75561f -r b89b63b2bc94 test/classes/@CPrecedenceTester2/double.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/classes/@CPrecedenceTester2/double.m	Wed Sep 26 14:10:32 2018 +0200
@@ -0,0 +1,5 @@
+function x = double (snk)
+
+  x = snk.useless_data;
+
+end
diff -r 49734a75561f -r b89b63b2bc94 test/classes/@CPrecedenceTester2/plus.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/classes/@CPrecedenceTester2/plus.m	Wed Sep 26 14:10:32 2018 +0200
@@ -0,0 +1,5 @@
+function s = plus (s1, s2)
+
+  s = 'CPrecedenceTester2';
+
+end
diff -r 49734a75561f -r b89b63b2bc94 test/classes/@CPrecedenceTester3/double.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/classes/@CPrecedenceTester3/double.m	Wed Sep 26 14:10:32 2018 +0200
@@ -0,0 +1,5 @@
+function x = double (snk)
+
+  x = snk.useless_data;
+
+end
diff -r 49734a75561f -r b89b63b2bc94 test/classes/@CPrecedenceTester3/plus.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/classes/@CPrecedenceTester3/plus.m	Wed Sep 26 14:10:32 2018 +0200
@@ -0,0 +1,5 @@
+function s = plus (s1, s2)
+
+  s = 'CPrecedenceTester3';
+
+end
diff -r 49734a75561f -r b89b63b2bc94 test/classes/classes.tst
--- a/test/classes/classes.tst	Thu Sep 13 11:04:57 2018 -0700
+++ b/test/classes/classes.tst	Wed Sep 26 14:10:32 2018 +0200
@@ -343,18 +343,22 @@
 %% Testing precedence %%
 %%%%%%%%%%%%%%%%%%%%%%%%
 
-%% default: leftmost object wins
+%% default: leftmost or superior class (bug #42735) object wins
 %!shared A, B
 %!test A = Snork (rand (2));
 %!test B = CPrecedenceTester1 ();  % no call to inferiorto/superiorto
 %!assert (isequal (tattack (A, B), 'Snork'))
 %!assert (isequal (tattack (B, A), 'CPrecedenceTester1'))  % idem
+%!assert <*42735> (isequal (class (A + B), 'Snork'))
+%!assert <*42735> (isequal (B + A, 'CPrecedenceTester1'))  % idem
 
 %!shared A, B
 %!test A = Snork (rand (2));
 %!test B = CPrecedenceTester2 (1);  % CPrecedenceTester2 > Snork
 %!assert (isequal (tattack (A, B), 'CPrecedenceTester2'))
 %!assert (isequal (tattack (B, A), 'CPrecedenceTester2'))
+%!assert <*42735> (isequal (A + B, 'CPrecedenceTester2'))
+%!assert <*42735> (isequal (B + A, 'CPrecedenceTester2'))
 %% Trying to change to CPrecendenceTester < Snork
 %!error D = CPrecedenceTester2 (2)
 
@@ -363,6 +367,8 @@
 %!test B = CPrecedenceTester3 (2);  % CPrecedenceTester3 < Snork
 %!assert (isequal (tattack (A, B), 'Snork'))
 %!assert (isequal (tattack (B, A), 'Snork'))
+%!assert <*42735> (isequal (class (A + B), 'Snork'))
+%!assert <*42735> (isequal (class (B + A), 'Snork'))
 %% Trying to change to CPrecendenceTester3 > Snork
 %!error D = CPrecedenceTester3 (1)
 
