changeset 32446:d0b363fd92c5

Support to few return values from anon. func. in bc-vm There was a bug when a constant as root expression would not produce enough return values. E.g. 'h = @() 1; [a b] = h();' would trigger an internal assert. * pt-bytecode-vm.cc: Accept 0 <= k <= nargout ret. values * bytecode_anon_handles.m: Update test
author Petter T.
date Fri, 27 Oct 2023 18:20:09 +0200
parents 4d6615bca5b4
children 53920541c0b8
files libinterp/parse-tree/pt-bytecode-vm.cc test/compile/bytecode_anon_handles.m
diffstat 2 files changed, 39 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/parse-tree/pt-bytecode-vm.cc	Fri Oct 27 18:20:03 2023 +0200
+++ b/libinterp/parse-tree/pt-bytecode-vm.cc	Fri Oct 27 18:20:09 2023 +0200
@@ -5972,16 +5972,19 @@
 
     assert (N_RETURNS () == -128);
 
-    int n_returns_callee = bsp[0].i + 1; // Nargout on stack, +1 to include %nargout
-    if (n_returns_callee == 1)
-      n_returns_callee = 2;
+    int n_returns_callee = bsp[0].i; // Nargout on stack
+    if (n_returns_callee == 0)
+      n_returns_callee = 1;
 
     int n_locals_callee = N_LOCALS (); // Amount of arguments, purely local variables and %nargout
 
-    // Assert that the stack pointer is back where it should be
-    assert (bsp + n_locals_callee +  n_returns_callee - 1 == sp);
-
-    stack_element *first_ret = sp - n_returns_callee + 1;
+    int n_ret_on_stack = sp - bsp - n_locals_callee;
+
+    // Assert that the stack pointer is back where it should be, i.e. that there are between
+    // zero and nargout return values.
+    assert (n_ret_on_stack >= 0 && n_ret_on_stack <= n_returns_callee);
+
+    stack_element *first_ret = sp - n_ret_on_stack;
 
     // Destroy locals
     //
@@ -6027,16 +6030,16 @@
         int j;
         // nargout 0 should still give one return value, if there is one
         int n_root_wanted = std::max (root_nargout, 1);
-        for (j = 1; j < n_returns_callee && j < (n_root_wanted + 1); j++)
+        for (j = 0; j < n_ret_on_stack && j < n_root_wanted; j++)
           {
-            int idx = (n_returns_callee - 1) - j;
+            int idx = n_ret_on_stack - 1 - j;
             ret.append (std::move (first_ret[idx].ov));
             first_ret[idx].ov.~octave_value ();
           }
         // Destroy rest of return values, if any
-        for (; j < n_returns_callee; j++)
+        for (; j < n_ret_on_stack; j++)
           {
-            int idx = (n_returns_callee - 1) - j;
+            int idx = n_ret_on_stack - j;
             first_ret[idx].ov.~octave_value ();
           }
 
@@ -6093,7 +6096,7 @@
     // Move the callee's return values to the top of the stack of the caller.
     // Renaming variables to keep my sanity.
     int n_args_caller_expects = caller_nval_back;
-    int n_args_callee_has = n_returns_callee - 1; // Excludes %nargout
+    int n_args_callee_has = n_ret_on_stack; // Excludes %nargout
     int n_args_to_move = std::min (n_args_caller_expects, n_args_callee_has);
     int n_args_actually_moved = 0;
 
--- a/test/compile/bytecode_anon_handles.m	Fri Oct 27 18:20:03 2023 +0200
+++ b/test/compile/bytecode_anon_handles.m	Fri Oct 27 18:20:09 2023 +0200
@@ -107,6 +107,30 @@
   assert (h2 (3) == 6)
   h2 = @(yy) execute_handle (@(yyyy) execute_handle (@(yyy) h1 (yyy), yyyy), yy); % Nest some more
   assert (h2 (3) == 6)
+
+  % Test not enough return values
+  threw = false;
+  try
+    h1 = @() 1;
+    [a, b] = h1 ();
+  catch
+    threw = true;
+  end
+
+  assert (threw);
+
+  h1 = @(varargin) varargin{:};
+  [a b c] = h1(1,2,3);
+  assert ([a b c] == [1 2 3])
+
+  threw = false;
+  try
+    [a b c d] = h1(1,2,3);
+  catch
+    threw = true;
+  end
+
+  assert (threw);
 end
 
 function [x, y, z] = try_isargout ()