changeset 32796:5252c3621c7a

merge stable to default
author John W. Eaton <jwe@octave.org>
date Sat, 20 Jan 2024 14:00:02 -0500
parents 0c193dadf110 (current diff) aee5333aebb1 (diff)
children 75d34a95da34 ffff22bd5e5e
files libinterp/corefcn/stack-frame.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h
diffstat 10 files changed, 179 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/func.txi	Fri Jan 19 16:34:31 2024 -0800
+++ b/doc/interpreter/func.txi	Sat Jan 20 14:00:02 2024 -0500
@@ -628,6 +628,25 @@
 @end group
 @end example
 
+Functions may take advantage of ignored outputs to reduce the number of
+calculations performed.  To do so, use the @code{isargout} function to query
+whether the output argument is wanted.  For example:
+
+@example
+@group
+function [out1, out2] = long_function (x, y, z)
+  if (isargout (1))
+    ## Long calculation
+    @dots{}
+    out1 = result;
+  endif
+  @dots{}
+endfunction
+@end group
+@end example
+
+@DOCSTRING(isargout)
+
 @node Default Arguments
 @section Default Arguments
 @cindex default arguments
--- a/etc/NEWS.9.md	Fri Jan 19 16:34:31 2024 -0800
+++ b/etc/NEWS.9.md	Sat Jan 20 14:00:02 2024 -0500
@@ -173,12 +173,6 @@
 
         Function               | Replacement
         -----------------------|------------------
-        isargout               | none (see below)
-
-    * The implementation of `isargout` has been the source of a number of bugs
-      and was inefficient, even for functions that did not use it.  It may be
-      removed in a future version of Octave.  Until then, `isargout(k)` will
-      return true for k in the range `1:min (1, nargout)`.
 
 - Properties
 
--- a/libinterp/corefcn/stack-frame.h	Fri Jan 19 16:34:31 2024 -0800
+++ b/libinterp/corefcn/stack-frame.h	Sat Jan 20 14:00:02 2024 -0500
@@ -130,6 +130,7 @@
   enum auto_var_type
   {
     ARG_NAMES,
+    IGNORED,
     NARGIN,
     NARGOUT,
     SAVED_WARNING_STATES,
--- a/libinterp/octave-value/ov-usr-fcn.cc	Fri Jan 19 16:34:31 2024 -0800
+++ b/libinterp/octave-value/ov-usr-fcn.cc	Sat Jan 20 14:00:02 2024 -0500
@@ -790,7 +790,7 @@
 
 Programming Note.  @code{nargout} does not work for built-in functions and
 returns -1 for all anonymous functions.
-@seealso{nargin, varargout, nthargout}
+@seealso{nargin, varargout, isargout, nthargout}
 @end deftypefn */)
 {
   int nargin = args.length ();
@@ -886,27 +886,39 @@
 }
 
 static bool
-isargout1 (int nargout, double k)
+val_in_table (const Matrix& table, double val)
+{
+  if (table.isempty ())
+    return false;
+
+  octave_idx_type i = table.lookup (val, ASCENDING);
+  return (i > 0 && table(i-1) == val);
+}
+
+static bool
+isargout1 (int nargout, const Matrix& ignored, double k)
 {
   if (k != math::fix (k) || k <= 0)
     error ("isargout: K must be a positive integer");
 
-  return k == 1 || k <= nargout;
+  return (k == 1 || k <= nargout) && ! val_in_table (ignored, k);
 }
 
-// LEGACY FUNCTION as of Octave 9.
-
 DEFMETHOD (isargout, interp, args, ,
            doc: /* -*- texinfo -*-
 @deftypefn {} {@var{tf} =} isargout (@var{k})
-This function is obsolete and may be removed from a future version of
-Octave.  Inside a function and now always returns true for @var{k} in
-the range @code{1:min (1, nargout)}.
+Within a function, return a logical value indicating whether the argument
+@var{k} will be assigned to a variable on output.
 
-@var{k} can also be an array, in which case the function works
-element-by-element and a logical array is returned.
+If the result is false, the argument has been ignored during the function
+call through the use of the tilde (~) special output argument.  Functions
+can use @code{isargout} to avoid performing unnecessary calculations for
+outputs which are unwanted.
 
-At the top level, @code{isargout} returns an error.
+If @var{k} is outside the range @code{1:max (nargout)}, the function returns
+false.  @var{k} can also be an array, in which case the function works
+element-by-element and a logical array is returned.  At the top level,
+@code{isargout} returns an error.
 @seealso{nargout, varargout, nthargout}
 @end deftypefn */)
 {
@@ -925,11 +937,16 @@
   if (tmp.is_defined ())
     nargout1 = tmp.int_value ();
 
+  Matrix ignored;
+  tmp = tw.get_auto_fcn_var (stack_frame::IGNORED);
+  if (tmp.is_defined ())
+    ignored = tmp.matrix_value ();
+
   if (args(0).is_scalar_type ())
     {
       double k = args(0).double_value ();
 
-      return ovl (isargout1 (nargout1, k));
+      return ovl (isargout1 (nargout1, ignored, k));
     }
   else if (args(0).isnumeric ())
     {
@@ -937,7 +954,7 @@
 
       boolNDArray r (ka.dims ());
       for (octave_idx_type i = 0; i < ka.numel (); i++)
-        r(i) = isargout1 (nargout1, ka(i));
+        r(i) = isargout1 (nargout1, ignored, ka(i));
 
       return ovl (r);
     }
@@ -949,50 +966,83 @@
 
 /*
 %!function [x, y] = try_isargout ()
-%!  global isargout_global
-%!  isargout_global = [isargout(1), isargout(2)];
-%!  x = 1;
-%!  y = 2;
+%!  if (isargout (1))
+%!    if (isargout (2))
+%!      x = 1; y = 2;
+%!    else
+%!      x = -1;
+%!    endif
+%!  else
+%!    if (isargout (2))
+%!      y = -2;
+%!    else
+%!      error ("no outputs requested");
+%!    endif
+%!  endif
+%!endfunction
+%!
+%!function [a, b] = try_isargout2 (x, y)
+%!  a = y;
+%!  b = {isargout(1), isargout(2), x};
 %!endfunction
 %!
 %!test
-%! global isargout_global
-%! unwind_protect
-%!   [x, y] = try_isargout ();
-%!   assert ([x, y], [1, 2]);
-%!   assert (isargout_global, [true, true]);
-%! unwind_protect_cleanup
-%!   clear -global isargout_global
-%! end_unwind_protect
+%! [x, y] = try_isargout ();
+%! assert ([x, y], [1, 2]);
+%!
+%!test
+%! [x, ~] = try_isargout ();
+%! assert (x, -1);
 %!
 %!test
-%! global isargout_global
-%! unwind_protect
-%!   [x, ~] = try_isargout ();
-%!   assert (x, 1);
-%!   assert (isargout_global, [true, true]);
-%! unwind_protect_cleanup
-%!   clear -global isargout_global
-%! end_unwind_protect
+%! [~, y] = try_isargout ();
+%! assert (y, -2);
+%!
+%!error [~, ~] = try_isargout ()
+%!
+## Check to see that isargout isn't sticky:
+%!test
+%! [x, y] = try_isargout ();
+%! assert ([x, y], [1, 2]);
 %!
+## It should work without ():
+%!test
+%! [~, y] = try_isargout;
+%! assert (y, -2);
+%!
+## It should work in function handles,
+%!test
+%! fh = @try_isargout;
+%! [~, y] = fh ();
+%! assert (y, -2);
+%!
+## anonymous functions,
 %!test
-%! global isargout_global
-%! unwind_protect
-%!   [~, y] = try_isargout ();
-%!   assert (y, 2);
-%!   assert (isargout_global, [true, true]);
-%! unwind_protect_cleanup
-%!   clear -global isargout_global
-%! end_unwind_protect
+%! af = @() try_isargout;
+%! [~, y] = af ();
+%! assert (y, -2);
 %!
+## and cell arrays of handles or anonymous functions.
 %!test
-%! global isargout_global
-%! unwind_protect
-%!   [~, ~] = try_isargout ()
-%!   assert (isargout_global, [true, true]);
-%! unwind_protect_cleanup
-%!   clear -global isargout_global
-%! end_unwind_protect
+%! fh = @try_isargout;
+%! af = @() try_isargout;
+%! c = {fh, af};
+%! [~, y] = c{1}();
+%! assert (y, -2);
+%!test
+%! fh = @try_isargout;
+%! af = @() try_isargout;
+%! c = {fh, af};
+%! [~, y] = c{2}();
+%! assert (y, -2);
+%!
+## Nesting, anyone?
+%!test
+%! [~, b] = try_isargout2 (try_isargout, rand);
+%! assert (b, {0, 1, -1});
+%!test
+%! [~, b] = try_isargout2 ({try_isargout, try_isargout}, rand);
+%! assert (b, {0, 1, {-1, -1}});
 */
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/op-kw-docs	Fri Jan 19 16:34:31 2024 -0800
+++ b/libinterp/op-kw-docs	Sat Jan 20 14:00:02 2024 -0500
@@ -873,14 +873,14 @@
 -*- texinfo -*-
 @deftypefn {} {} varargin
 Pass an arbitrary number of arguments into a function.
-@seealso{varargout, nargin, nargout, nthargout}
+@seealso{varargout, nargin, isargout, nargout, nthargout}
 @end deftypefn
 varargout
 @c libinterp/parse-tree/oct-parse.yy
 -*- texinfo -*-
 @deftypefn {} {} varargout
 Pass an arbitrary number of arguments out of a function.
-@seealso{varargin, nargin, nargout, nthargout}
+@seealso{varargin, nargin, isargout, nargout, nthargout}
 @end deftypefn
 while
 @c libinterp/parse-tree/oct-parse.yy
--- a/libinterp/parse-tree/pt-assign.cc	Fri Jan 19 16:34:31 2024 -0800
+++ b/libinterp/parse-tree/pt-assign.cc	Sat Jan 20 14:00:02 2024 -0500
@@ -207,8 +207,6 @@
                                          ? rhs_val1(0).list_value ()
                                          : rhs_val1);
 
-      tw.set_lvalue_list (nullptr);
-
       octave_idx_type k = 0;
 
       octave_idx_type n = rhs_val.length ();
--- a/libinterp/parse-tree/pt-eval.cc	Fri Jan 19 16:34:31 2024 -0800
+++ b/libinterp/parse-tree/pt-eval.cc	Sat Jan 20 14:00:02 2024 -0500
@@ -1458,6 +1458,40 @@
   return frame->inputname (n, ids_only);
 }
 
+Matrix
+tree_evaluator::ignored_fcn_outputs () const
+{
+  Matrix retval;
+
+  const std::list<octave_lvalue> *lvalues = m_lvalue_list;
+
+  if (! lvalues)
+    return retval;
+
+  octave_idx_type nbh = 0;
+
+  for (const auto& lval : *lvalues)
+    nbh += lval.is_black_hole ();
+
+  if (nbh > 0)
+    {
+      retval.resize (1, nbh);
+
+      octave_idx_type k = 0;
+      octave_idx_type l = 0;
+
+      for (const auto& lval : *lvalues)
+        {
+          if (lval.is_black_hole ())
+            retval(l++) = k+1;
+
+          k += lval.numel ();
+        }
+    }
+
+  return retval;
+}
+
 // If NAME is an operator (like "+", "-", ...), convert it to the
 // corresponding function name ("plus", "minus", ...).
 
@@ -3529,6 +3563,17 @@
 
   octave_value_list args (xargs);
 
+  // FIXME: this probably shouldn't be a double-precision matrix.
+  Matrix ignored_outputs = ignored_fcn_outputs ();
+
+  unwind_protect frame;
+
+  if (! user_function.is_anonymous_function ())
+    {
+      frame.protect_var (m_lvalue_list);
+      m_lvalue_list = nullptr;
+    }
+
   octave_value_list ret_args;
 
   int nargin = args.length ();
@@ -3591,8 +3636,8 @@
         }
     }
 
-  bind_auto_fcn_vars (xargs.name_tags (), nargin, nargout,
-                      user_function.takes_varargs (),
+  bind_auto_fcn_vars (xargs.name_tags (), ignored_outputs, nargin,
+                      nargout, user_function.takes_varargs (),
                       user_function.all_va_args (args));
 
   // For classdef constructor, pre-populate the output arguments
@@ -5192,11 +5237,13 @@
 
 void
 tree_evaluator::bind_auto_fcn_vars (const string_vector& arg_names,
+                                    const Matrix& ignored_outputs,
                                     int nargin, int nargout,
                                     bool takes_varargs,
                                     const octave_value_list& va_args)
 {
   set_auto_fcn_var (stack_frame::ARG_NAMES, Cell (arg_names));
+  set_auto_fcn_var (stack_frame::IGNORED, ignored_outputs);
   set_auto_fcn_var (stack_frame::NARGIN, nargin);
   set_auto_fcn_var (stack_frame::NARGOUT, nargout);
   set_auto_fcn_var (stack_frame::SAVED_WARNING_STATES, octave_value ());
@@ -5631,12 +5678,4 @@
 %!error <N must be a scalar index> inputname (-1)
 */
 
-// DEPRECATED in Octave 9.
-
-Matrix
-tree_evaluator::ignored_fcn_outputs () const
-{
-  return Matrix ();
-}
-
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-eval.h	Fri Jan 19 16:34:31 2024 -0800
+++ b/libinterp/parse-tree/pt-eval.h	Sat Jan 20 14:00:02 2024 -0500
@@ -344,7 +344,6 @@
     SC_OTHER      // command-line input or eval string
   };
 
-  OCTAVE_DEPRECATED (9, "tree_evaluator::ignored_fcn_outputs is obsolete and now always returns an empty Matrix object");
   Matrix ignored_fcn_outputs () const;
 
   std::string inputname (int n, bool ids_only = true) const;
@@ -889,7 +888,8 @@
   bool quit_loop_now ();
 
   void bind_auto_fcn_vars (const string_vector& arg_names,
-                           int nargin, int nargout, bool takes_varargs,
+                           const Matrix& ignored_outputs, int nargin,
+                           int nargout, bool takes_varargs,
                            const octave_value_list& va_args);
 
   std::string check_autoload_file (const std::string& nm) const;
--- a/libinterp/parse-tree/pt-mat.cc	Fri Jan 19 16:34:31 2024 -0800
+++ b/libinterp/parse-tree/pt-mat.cc	Sat Jan 20 14:00:02 2024 -0500
@@ -47,6 +47,13 @@
 octave_value
 tree_matrix::evaluate (tree_evaluator& tw, int)
 {
+  unwind_action act ([&tw] (const std::list<octave_lvalue> *lvl)
+  {
+    tw.set_lvalue_list (lvl);
+  }, tw.lvalue_list ());
+
+  tw.set_lvalue_list (nullptr);
+
   tm_const tmp (*this, tw);
 
   return tmp.concat (tw.string_fill_char ());
--- a/scripts/miscellaneous/nthargout.m	Fri Jan 19 16:34:31 2024 -0800
+++ b/scripts/miscellaneous/nthargout.m	Sat Jan 20 14:00:02 2024 -0500
@@ -68,7 +68,7 @@
 ## @var{USV} = nthargout ([1:3], @@svd, hilb (5));
 ## @end example
 ##
-## @seealso{nargin, nargout, varargin, varargout}
+## @seealso{nargin, nargout, varargin, varargout, isargout}
 ## @end deftypefn
 
 function arg = nthargout (n, varargin)