changeset 28427:7a8c69c4eb55 stable

convert obsolete octave_fcn_inline object to @inline class Use a legacy @class object to provide inline function objects instead of using a built-in class derived from the octave_function_handle class. * scripts/legacy/@inline: New directory containing the following files: argnames.m, char.m, feval.m, formula.m, inline.m, subsref.m, vectorize.m. * scripts/legacy/@inline/module.mk: New file. * scripts/legacy/module.mk, scripts/module.mk: Update. * test/inline-fcn.tst: New tests. * test/module.mk: Update. * ov-fcn-inline.h, ov-fcn-inline.cc: Delete. Remove all uses. * libinterp/octave-value/module.mk: Update. * external.txi, func.txi, octave.texi, plot.txi, quad.txi: Eliminate discusion of inline function objects. * fplot.m, __ezplot__.m: Use isa to identify inline objects instead of instead of checking typeinfo. * ov-fcn-handle.cc, ov-typeinfo.cc: Fix tests. * ov-base.h, ov-base.cc (octave_base_value::fcn_inline_value): Delete. * ov.h, ov.cc (octave_value::fcn_inline_value): Delete. (octave_value::xfcn_inline_value): Delete value extractor. * ov-class.cc, ov-class.h (octave_inline, octave_inline_fcn): New classes that allow us to preserve the is_inline_function and function_value methods that were previously available in the octave_fcn_inline class. (F__inline_ctor__): New function for final construction of inline objects. * ls-hdf5.cc, ls-mat5.cc, ls-oct-binary.cc, ls-oct-text.cc: Handle loading of inline function objects from old files as special cases. New inline function objects will be saved and loaded as ordinary @class objects.
author John W. Eaton <jwe@octave.org>
date Mon, 23 Mar 2020 15:29:49 -0400
parents 9a3deb17b4ea
children e9a12be5fd79
files doc/interpreter/external.txi doc/interpreter/func.txi doc/interpreter/octave.texi doc/interpreter/plot.txi doc/interpreter/quad.txi libinterp/corefcn/cellfun.cc libinterp/corefcn/interpreter-private.cc libinterp/corefcn/interpreter.cc libinterp/corefcn/ls-hdf5.cc libinterp/corefcn/ls-mat5.cc libinterp/corefcn/ls-oct-binary.cc libinterp/corefcn/ls-oct-text.cc libinterp/octave-value/module.mk libinterp/octave-value/ov-base.cc libinterp/octave-value/ov-base.h libinterp/octave-value/ov-class.cc libinterp/octave-value/ov-class.h libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-fcn-inline.cc libinterp/octave-value/ov-fcn-inline.h libinterp/octave-value/ov-typeinfo.cc libinterp/octave-value/ov.cc libinterp/octave-value/ov.h scripts/legacy/@inline/argnames.m scripts/legacy/@inline/char.m scripts/legacy/@inline/feval.m scripts/legacy/@inline/formula.m scripts/legacy/@inline/inline.m scripts/legacy/@inline/module.mk scripts/legacy/@inline/subsref.m scripts/legacy/@inline/vectorize.m scripts/legacy/module.mk scripts/module.mk scripts/plot/draw/fplot.m scripts/plot/draw/private/__ezplot__.m test/inline-fcn.tst test/module.mk
diffstat 37 files changed, 1020 insertions(+), 1264 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/external.txi	Sat Apr 25 13:17:11 2020 -0400
+++ b/doc/interpreter/external.txi	Mon Mar 23 15:29:49 2020 -0400
@@ -917,8 +917,6 @@
 
 @item Anonymous Function Handle
 
-@item Inline Function
-
 @item String
 @end enumerate
 
@@ -938,8 +936,6 @@
 @result{} 0.84147
 funcdemo (@@(x) sin (x), 1)
 @result{} 0.84147
-funcdemo (inline ("sin (x)"), 1)
-@result{} 0.84147
 funcdemo ("sin", 1)
 @result{} 0.84147
 funcdemo (@@atan2, 1, 1)
@@ -1698,8 +1694,7 @@
 @end group
 @end example
 
-Note that it is not possible to use function handles or inline functions
-within a mex-file.
+Note that it is not possible to use function handles within a mex-file.
 
 @c @node Application Programming Interface for Mex-Files
 @c @subsection Application Programming Interface for Mex-Files
--- a/doc/interpreter/func.txi	Sat Apr 25 13:17:11 2020 -0400
+++ b/doc/interpreter/func.txi	Mon Mar 23 15:29:49 2020 -0400
@@ -40,7 +40,7 @@
 * Validating Arguments::
 * Function Files::
 * Script Files::
-* Function Handles Anonymous Functions Inline Functions::
+* Function Handles and Anonymous Functions::
 * Commands::
 * Organization of Functions::
 @end menu
@@ -1733,11 +1733,10 @@
 @end group
 @end example
 
-@node Function Handles Anonymous Functions Inline Functions
-@section Function Handles, Anonymous Functions, Inline Functions
+@node Function Handles and Anonymous Functions
+@section Function Handles and Anonymous Functions
 @cindex handle, function handles
 @cindex anonymous functions
-@cindex inline, inline functions
 
 It can be very convenient store a function in a variable so that it
 can be passed to a different function.  For example, a function that
@@ -1747,7 +1746,6 @@
 @menu
 * Function Handles::
 * Anonymous Functions::
-* Inline Functions::
 @end menu
 
 @node Function Handles
@@ -1887,34 +1885,6 @@
 @xref{Operator Overloading}, for a list of operators which also have a
 functional form.
 
-@node Inline Functions
-@subsection Inline Functions
-
-An inline function is created from a string containing the function
-body using the @code{inline} function.  The following code defines the
-function @math{f(x) = x^2 + 2}.
-
-@example
-f = inline ("x^2 + 2");
-@end example
-
-@noindent
-After this it is possible to evaluate @math{f} at any @math{x} by
-writing @code{f(x)}.
-
-@strong{Caution}: @sc{matlab} has begun the process of deprecating inline
-functions.  At some point in the future support will be dropped and eventually
-Octave will follow @sc{matlab} and also remove inline functions.  Use anonymous
-functions in all new code.
-
-@DOCSTRING(inline)
-
-@DOCSTRING(argnames)
-
-@DOCSTRING(formula)
-
-@DOCSTRING(symvar)
-
 @node Commands
 @section Commands
 
--- a/doc/interpreter/octave.texi	Sat Apr 25 13:17:11 2020 -0400
+++ b/doc/interpreter/octave.texi	Mon Mar 23 15:29:49 2020 -0400
@@ -411,7 +411,7 @@
 * Default Arguments::
 * Function Files::
 * Script Files::
-* Function Handles Anonymous Functions Inline Functions::
+* Function Handles and Anonymous Functions::
 * Commands::
 * Organization of Functions::
 
@@ -446,11 +446,10 @@
 * HTML Markup::
 * LaTeX Markup::
 
-Function Handles Anonymous Functions Inline Functions
+Function Handles and Anonymous Functions
 
 * Function Handles::
 * Anonymous Functions::
-* Inline Functions::
 
 Errors and Warnings
 
--- a/doc/interpreter/plot.txi	Sat Apr 25 13:17:11 2020 -0400
+++ b/doc/interpreter/plot.txi	Mon Mar 23 15:29:49 2020 -0400
@@ -330,12 +330,11 @@
 @subsubsection Two-dimensional Function Plotting
 @cindex plotting, two-dimensional functions
 
-Octave can plot a function from a function handle, inline function, or
-string defining the function without the user needing to explicitly
-create the data to be plotted.  The function @code{fplot} also generates
-two-dimensional plots with linear axes using a function name and limits
-for the range of the x-coordinate instead of the x and y data.  For
-example,
+Octave can plot a function from a function handle or string defining the
+function without the user needing to explicitly create the data to be
+plotted.  The function @code{fplot} also generates two-dimensional plots
+with linear axes using a function name and limits for the range of the
+x-coordinate instead of the x and y data.  For example,
 
 @example
 @group
@@ -1871,7 +1870,7 @@
 as an anonymous function, or as a string representing an Octave command.  The
 latter syntax is not recommended since syntax errors will only occur when the
 string is evaluated.
-@xref{Function Handles Anonymous Functions Inline Functions, , Function Handles section}.
+@xref{Function Handles and Anonymous Functions, , Function Handles section}.
 
 This can then be associated with an object either at the object's creation, or
 later with the @code{set} function.  For example,
--- a/doc/interpreter/quad.txi	Sat Apr 25 13:17:11 2020 -0400
+++ b/doc/interpreter/quad.txi	Mon Mar 23 15:29:49 2020 -0400
@@ -140,18 +140,12 @@
 is reasonably accurate (to see why, examine what happens to the result
 if you move the lower bound to 0.1, then 0.01, then 0.001, etc.).
 
-The function @qcode{"f"} can be the string name of a function, a function
-handle, or an inline function.  These options make it quite easy to do
-integration without having to fully define a function in an m-file.  For
-example:
+The function @qcode{"f"} can be the string name of a function or a
+function handle.  These options make it quite easy to do integration
+without having to fully define a function in an m-file.  For example:
 
 @example
 @group
-# Verify integral (x^3) = x^4/4
-f = inline ("x.^3");
-quadgk (f, 0, 1)
-     @result{} 0.25000
-
 # Verify gamma function = (n-1)! for n = 4
 f = @@(x) x.^3 .* exp (-x);
 quadcc (f, 0, Inf)
--- a/libinterp/corefcn/cellfun.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/corefcn/cellfun.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -452,7 +452,7 @@
   // more specific function class, so this can result in fewer polymorphic
   // function calls as the function gets called for each value of the array.
   {
-    if (func.is_function_handle ())
+    if (func.is_function_handle () || func.is_inline_function ())
       {
         // We can't check for overloads now.  Is there something else we
         // should be doing instead?
@@ -1156,7 +1156,7 @@
 
       if (! symbol_table_lookup)
         {
-          if (func.is_function_handle ())
+          if (func.is_function_handle () || func.class_name () == "inline")
             {
               // We can't check for overloads now.  Is there something
               // else we should be doing instead?
--- a/libinterp/corefcn/interpreter-private.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/corefcn/interpreter-private.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -44,7 +44,7 @@
 #include "load-path.h"
 #include "load-save.h"
 #include "ov.h"
-#include "ov-fcn-inline.h"
+#include "ovl.h"
 #include "pager.h"
 #include "symtab.h"
 
@@ -208,6 +208,9 @@
     return get_function_handle (interp, arg, parameter_names);
   }
 
+  // May return a function handle object, inline function object, or
+  // function object.
+
   octave_value
   get_function_handle (interpreter& interp, const octave_value& arg,
                        const std::list<std::string>& parameter_names)
@@ -228,12 +231,19 @@
         if (fcn.is_defined ())
           return fcn;
 
-        fcn = octave_value (new octave_fcn_inline (fstr, parameter_names));
-
         // Possibly warn here that passing the function body in a
         // character string is discouraged.
 
-        return fcn;
+        octave_value_list args (parameter_names.size () + 1);
+        octave_idx_type i = 0;
+        args(i++) = fstr;
+        for (const auto& pname : parameter_names)
+          args(i++) = pname;
+
+        octave_value_list tmp = interp.feval ("inline", args, 1);
+
+        if (tmp.length () > 0)
+          return tmp(0);
       }
 
     return octave_value ();
--- a/libinterp/corefcn/interpreter.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/corefcn/interpreter.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -1526,7 +1526,7 @@
       {
         return feval (val.function_value (), args, nargout);
       }
-    else if (val.is_function_handle ())
+    else if (val.is_function_handle () || val.is_inline_function ())
       {
         // This covers function handles, inline functions, and anonymous
         //  functions.
--- a/libinterp/corefcn/ls-hdf5.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/corefcn/ls-hdf5.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -54,6 +54,7 @@
 #include "defun.h"
 #include "error.h"
 #include "errwarn.h"
+#include "interpreter.h"
 #include "interpreter-private.h"
 #include "load-save.h"
 #include "oct-hdf5.h"
@@ -433,6 +434,212 @@
   return type_id;
 }
 
+static herr_t
+load_inline_fcn (hid_t loc_id, const char *name, octave_value& retval)
+{
+#if defined (HAVE_HDF5)
+
+  hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id;
+  hsize_t rank;
+  int slen;
+
+#if defined (HAVE_HDF5_18)
+  group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
+#else
+  group_hid = H5Gopen (loc_id, name);
+#endif
+  if (group_hid < 0) return -1;
+
+#if defined (HAVE_HDF5_18)
+  data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT);
+#else
+  data_hid = H5Dopen (group_hid, "args");
+#endif
+  space_hid = H5Dget_space (data_hid);
+  rank = H5Sget_simple_extent_ndims (space_hid);
+
+  if (rank != 2)
+    {
+      H5Dclose (data_hid);
+      H5Sclose (space_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
+  OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
+
+  H5Sget_simple_extent_dims (space_hid, hdims, maxdims);
+
+  octave_value_list args (hdims[1]+1);
+
+  OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]);
+
+  if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL,
+               octave_H5P_DEFAULT, s1) < 0)
+    {
+      H5Dclose (data_hid);
+      H5Sclose (space_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  H5Dclose (data_hid);
+  H5Sclose (space_hid);
+
+  for (size_t i = 0; i < hdims[1]; i++)
+    args(i+1) = std::string (s1 + i*hdims[0]);
+
+#if defined (HAVE_HDF5_18)
+  data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
+#else
+  data_hid = H5Dopen (group_hid, "nm");
+#endif
+
+  if (data_hid < 0)
+    {
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  type_hid = H5Dget_type (data_hid);
+  type_class_hid = H5Tget_class (type_hid);
+
+  if (type_class_hid != H5T_STRING)
+    {
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  space_hid = H5Dget_space (data_hid);
+  rank = H5Sget_simple_extent_ndims (space_hid);
+
+  if (rank != 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  slen = H5Tget_size (type_hid);
+  if (slen < 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
+
+  // create datatype for (null-terminated) string to read into:
+  st_id = H5Tcopy (H5T_C_S1);
+  H5Tset_size (st_id, slen);
+
+  if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
+               octave_H5P_DEFAULT, nm_tmp) < 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+  H5Tclose (st_id);
+  H5Dclose (data_hid);
+  // NAME is obsolete and unused.
+  // std::string name (nm_tmp);
+
+#if defined (HAVE_HDF5_18)
+  data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT);
+#else
+  data_hid = H5Dopen (group_hid, "iftext");
+#endif
+
+  if (data_hid < 0)
+    {
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  type_hid = H5Dget_type (data_hid);
+  type_class_hid = H5Tget_class (type_hid);
+
+  if (type_class_hid != H5T_STRING)
+    {
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  space_hid = H5Dget_space (data_hid);
+  rank = H5Sget_simple_extent_ndims (space_hid);
+
+  if (rank != 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  slen = H5Tget_size (type_hid);
+  if (slen < 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Dclose (data_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+
+  OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen);
+
+  // create datatype for (null-terminated) string to read into:
+  st_id = H5Tcopy (H5T_C_S1);
+  H5Tset_size (st_id, slen);
+
+  if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
+               octave_H5P_DEFAULT, iftext_tmp) < 0)
+    {
+      H5Sclose (space_hid);
+      H5Tclose (type_hid);
+      H5Gclose (group_hid);
+      return -1;
+    }
+  H5Tclose (st_id);
+  H5Dclose (data_hid);
+
+  args(0) = std::string (iftext_tmp);
+
+  octave::interpreter& interp
+    = octave::__get_interpreter__ ("load_inline_fcn");
+
+  octave_value_list tmp = interp.feval ("inline", args, 1);
+
+  if (tmp.length () > 0)
+    {
+      retval = tmp(0);
+      return 1;
+    }
+
+#else
+  octave_unused_parameter (loc_id);
+  octave_unused_parameter (name);
+  octave_unused_parameter (retval);
+
+  warn_load ("hdf5");
+#endif
+
+  return -1;
+}
+
 // This function is designed to be passed to H5Giterate, which calls it
 // on each data item in an HDF5 file.  For the item whose name is NAME in
 // the group GROUP_ID, this function sets dv->tc to an Octave representation
@@ -534,9 +741,16 @@
           H5Tclose (st_id);
           H5Dclose (data_id);
 
-          d->tc = type_info.lookup_type (typ);
+          if (std::string (typ) == "inline function")
+            {
+              retval = load_inline_fcn (subgroup_id, name, d->tc);
+            }
+          else
+            {
+              d->tc = type_info.lookup_type (typ);
 
-          retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1);
+              retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1);
+            }
 
           // check for OCTAVE_GLOBAL attribute:
           d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL");
@@ -545,6 +759,10 @@
         }
       else
         {
+          // It seems that this block only applies to an old list type
+          // and that we shouldn't need to handle the old inline
+          // function type here.
+
           // an HDF5 group is treated as an octave structure by
           // default (since that preserves name information), and an
           // octave list otherwise.
@@ -565,6 +783,10 @@
     }
   else if (info.type == H5G_DATASET && ident_valid)
     {
+      // It seems that this block only applies to an old version of
+      // Octave HDF5 files and that it is probably not important to
+      // handle the old inline function type here.
+
       // For backwards compatibility.
 #if defined (HAVE_HDF5_18)
       data_id = H5Dopen (group_id, name, octave_H5P_DEFAULT);
--- a/libinterp/corefcn/ls-mat5.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/corefcn/ls-mat5.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -65,7 +65,6 @@
 #include "oct-map.h"
 #include "ov-cell.h"
 #include "ov-class.h"
-#include "ov-fcn-inline.h"
 #include "ov.h"
 #include "ovl.h"
 #include "pager.h"
@@ -1174,60 +1173,50 @@
 
         if (isclass)
           {
-            if (classname == "inline")
+            octave::cdef_manager& cdm = interp.get_cdef_manager ();
+
+            if (cdm.find_class (classname, false, true).ok ())
               {
-                // inline is not an object in Octave but rather an
-                // overload of a function handle.  Special case.
-                tc = new octave_fcn_inline (m.contents ("expr")(0).string_value (),
-                                            m.contents ("args")(0).string_value ());
+                tc = m;
+                warning_with_id ("Octave:load:classdef-to-struct",
+                                 "load: classdef element has been converted to a struct");
               }
             else
               {
-                octave::cdef_manager& cdm = interp.get_cdef_manager ();
-
-                if (cdm.find_class (classname, false, true).ok ())
+                octave_class *cls
+                  = new octave_class (m, classname,
+                                      std::list<std::string> ());
+
+                if (cls->reconstruct_exemplar ())
                   {
-                    tc = m;
-                    warning_with_id ("Octave:load:classdef-to-struct",
-                                     "load: classdef element has been converted to a struct");
+
+                    if (! cls->reconstruct_parents ())
+                      warning_with_id ("Octave:load:classdef-object-inheritance",
+                                       "load: unable to reconstruct object inheritance");
+
+                    tc = cls;
+
+                    octave::load_path& lp = interp.get_load_path ();
+
+                    if (lp.find_method (classname, "loadobj") != "")
+                      {
+                        try
+                          {
+                            octave_value_list tmp = octave::feval ("loadobj", tc, 1);
+
+                            tc = tmp(0);
+                          }
+                        catch (const octave::execution_exception&)
+                          {
+                            goto data_read_error;
+                          }
+                      }
                   }
                 else
                   {
-                    octave_class *cls
-                      = new octave_class (m, classname,
-                                          std::list<std::string> ());
-
-                    if (cls->reconstruct_exemplar ())
-                      {
-
-                        if (! cls->reconstruct_parents ())
-                          warning_with_id ("Octave:load:classdef-object-inheritance",
-                                           "load: unable to reconstruct object inheritance");
-
-                        tc = cls;
-
-                        octave::load_path& lp = interp.get_load_path ();
-
-                        if (lp.find_method (classname, "loadobj") != "")
-                          {
-                            try
-                              {
-                                octave_value_list tmp = octave::feval ("loadobj", tc, 1);
-
-                                tc = tmp(0);
-                              }
-                            catch (const octave::execution_exception&)
-                              {
-                                goto data_read_error;
-                              }
-                          }
-                      }
-                    else
-                      {
-                        tc = m;
-                        warning_with_id ("Octave:load:classdef-to-struct",
-                                         "load: element has been converted to a structure");
-                      }
+                    tc = m;
+                    warning_with_id ("Octave:load:classdef-to-struct",
+                                     "load: element has been converted to a structure");
                   }
               }
           }
--- a/libinterp/corefcn/ls-oct-binary.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/corefcn/ls-oct-binary.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -44,6 +44,7 @@
 #include "defun.h"
 #include "error.h"
 #include "errwarn.h"
+#include "interpreter.h"
 #include "interpreter-private.h"
 #include "load-save.h"
 #include "ls-oct-binary.h"
@@ -56,6 +57,78 @@
 #include "variables.h"
 #include "version.h"
 
+static bool
+load_inline_fcn (std::istream& is, bool swap, octave::mach_info::float_format,
+                 octave_value& retval)
+{
+  int32_t nargs;
+  if (! is.read (reinterpret_cast<char *> (&nargs), 4))
+    return false;
+  if (swap)
+    swap_bytes<4> (&nargs);
+
+  if (nargs < 1)
+    return false;
+  else
+    {
+      int32_t tmp;
+      octave_value_list args (nargs+1);
+      for (int i = 0; i < nargs; i++)
+        {
+          if (! is.read (reinterpret_cast<char *> (&tmp), 4))
+            return false;
+          if (swap)
+            swap_bytes<4> (&tmp);
+
+          OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1);
+          is.read (ctmp, tmp);
+          args(i+1) = std::string (ctmp);
+
+          if (! is)
+            return false;
+        }
+
+      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
+        return false;
+      if (swap)
+        swap_bytes<4> (&tmp);
+
+      OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1);
+      is.read (ctmp1, tmp);
+      // NAME is obsolete and unused.
+      // std::string name (ctmp1);
+
+      if (! is)
+        return false;
+
+      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
+        return false;
+      if (swap)
+        swap_bytes<4> (&tmp);
+
+      OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
+      is.read (ctmp2, tmp);
+
+      if (is)
+        {
+          args(0) = std::string (ctmp2);
+
+          octave::interpreter& interp
+            = octave::__get_interpreter__ ("load_inline_fcn");
+
+          octave_value_list tmp = interp.feval ("inline", args, 1);
+
+          if (tmp.length () > 0)
+            {
+              retval = tmp(0);
+              return true;
+            }
+        }
+    }
+
+  return false;
+}
+
 // Extract one value (scalar, matrix, string, etc.) from stream IS and
 // place it in TC, returning the name of the variable.  If the value
 // is tagged as global in the file, return TRUE in GLOBAL.  If SWAP
@@ -238,6 +311,15 @@
           error ("load: trouble reading binary file '%s'", filename.c_str ());
         s[len] = '\0';
         std::string typ = s;
+
+        if (typ == "inline function")
+          {
+            // Special case for loading old octave_inline_fcn objects.
+            if (! load_inline_fcn (is, swap, fmt, tc))
+              error ("load: trouble reading binary file '%s'", filename.c_str ());
+            return retval;
+          }
+
         tc = type_info.lookup_type (typ);
       }
       break;
--- a/libinterp/corefcn/ls-oct-text.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/corefcn/ls-oct-text.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -52,6 +52,7 @@
 #include "defun.h"
 #include "error.h"
 #include "errwarn.h"
+#include "interpreter.h"
 #include "interpreter-private.h"
 #include "load-save.h"
 #include "ls-ascii-helper.h"
@@ -235,6 +236,53 @@
 
 #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == (t))
 
+static octave_value
+load_inline_fcn (std::istream& is, const std::string& filename)
+{
+  int nargs;
+  if (extract_keyword (is, "nargs", nargs, true))
+    {
+      std::string name;
+      octave_value_list args (nargs+1);
+      for (int i = 0; i < nargs; i++)
+        {
+          std::string tmp;
+          is >> tmp;
+          args(i+1) = tmp;
+        }
+      is >> name;
+      if (name == "0")
+        name = "";
+
+      skip_preceeding_newline (is);
+
+      std::string buf;
+
+      if (is)
+        {
+
+          // Get a line of text whitespace characters included,
+          // leaving newline in the stream.
+          buf = read_until_newline (is, true);
+        }
+
+      if (is)
+        {
+          args(0) = std::string (buf);
+
+          octave::interpreter& interp
+            = octave::__get_interpreter__ ("load_inline_fcn");
+
+          octave_value_list tmp = interp.feval ("inline", args, 1);
+
+          if (tmp.length () > 0)
+            return tmp(0);
+        }
+    }
+
+  error ("load: trouble reading ascii file '%s'", filename.c_str ());
+}
+
 std::string
 read_text_data (std::istream& is, const std::string& filename, bool& global,
                 octave_value& tc, octave_idx_type count,
@@ -280,6 +328,12 @@
   // Special case for backward compatibility.  A small bit of cruft
   if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array"))
     tc = charMatrix ();
+  else if (SUBSTRING_COMPARE_EQ (typ, 0, 15, "inline function"))
+    {
+      // Special case for loading old octave_inline_fcn objects.
+      tc = load_inline_fcn (is, filename);
+      return name;
+    }
   else
     {
       octave::type_info& type_info
--- a/libinterp/octave-value/module.mk	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/module.mk	Mon Mar 23 15:29:49 2020 -0400
@@ -43,7 +43,6 @@
   %reldir%/ov-cx-mat.h \
   %reldir%/ov-dld-fcn.h \
   %reldir%/ov-fcn-handle.h \
-  %reldir%/ov-fcn-inline.h \
   %reldir%/ov-fcn.h \
   %reldir%/ov-float.h \
   %reldir%/ov-flt-complex.h \
@@ -108,7 +107,6 @@
   %reldir%/ov-cx-mat.cc \
   %reldir%/ov-dld-fcn.cc \
   %reldir%/ov-fcn-handle.cc \
-  %reldir%/ov-fcn-inline.cc \
   %reldir%/ov-fcn.cc \
   %reldir%/ov-float.cc \
   %reldir%/ov-flt-complex.cc \
--- a/libinterp/octave-value/ov-base.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/ov-base.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -914,15 +914,6 @@
   return nullptr;
 }
 
-octave_fcn_inline *
-octave_base_value::fcn_inline_value (bool silent)
-{
-  if (! silent)
-    err_wrong_type_arg ("octave_base_value::fcn_inline_value()", type_name ());
-
-  return nullptr;
-}
-
 octave_value_list
 octave_base_value::list_value (void) const
 {
--- a/libinterp/octave-value/ov-base.h	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/ov-base.h	Mon Mar 23 15:29:49 2020 -0400
@@ -66,7 +66,6 @@
 class octave_user_script;
 class octave_user_code;
 class octave_fcn_handle;
-class octave_fcn_inline;
 class octave_value_list;
 
 enum builtin_type_t
@@ -631,8 +630,6 @@
 
   virtual octave_fcn_handle * fcn_handle_value (bool silent = false);
 
-  virtual octave_fcn_inline * fcn_inline_value (bool silent = false);
-
   virtual octave_value_list list_value (void) const;
 
   virtual octave_value convert_to_str (bool pad = false, bool force = false,
--- a/libinterp/octave-value/ov-class.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/ov-class.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -28,6 +28,7 @@
 #endif
 
 #include <istream>
+#include <memory>
 #include <ostream>
 
 #include "Array-util.h"
@@ -2028,3 +2029,117 @@
 
   return octave_value();
 }
+
+// The following classes allow us to define "inline" function objects as
+// legacy @class objects (as they appear to be in Matlab) while
+// preserving the is_inline_function and function_value methods that
+// were previously available in the octave_fcn_inline class.  However,
+// inline function objects no longer behave as octave_fcn_handle objects
+// so calling is_function_handle for them no longer returns true.  I see
+// no reasonable way to preserve that behavior.  The goal here is to
+// allow most code that used the old octave_inline_fcn object to
+// continue to work while eliminating the octave_inline_fcn class that
+// was derived from the octave_fcn_handle class.  Making that change
+// appears to be necessary to properly fix function handle behavior and
+// improve Matlab compatibility.  It's unfortunate if this change causes
+// trouble, but I see no better fix.  Ultimately, we should replace all
+// uses of "inline" function objects with anonymous functions.
+
+class octave_inline;
+
+// The following class can be removed once the
+// octave_value::function_value method is removed.
+
+class
+octave_inline_fcn : public octave_function
+{
+public:
+
+  octave_inline_fcn (octave_inline *obj) : m_inline_obj (obj) { }
+
+  // No copying!
+
+  octave_inline_fcn (const octave_inline_fcn& ob) = delete;
+
+  octave_inline_fcn& operator = (const octave_inline_fcn& ob) = delete;
+
+  ~octave_inline_fcn (void) = default;
+
+  // We don't need to override both forms of the call method.  The using
+  // declaration will avoid warnings about partially-overloaded virtual
+  // functions.
+  using octave_function::call;
+
+  octave_value_list
+  call (octave::tree_evaluator& tw, int nargout = 0,
+        const octave_value_list& args = octave_value_list ());
+
+private:
+
+  octave_inline *m_inline_obj;
+};
+
+// Once the octave_inline_fcn class is removed, we should also be able
+// to eliminate the octave_inline class below and replace the
+// octave_value::is_inline_function method with
+//
+// bool octave_value::is_inline_function (void) const
+// {
+//   return class_name () == "inline";
+// }
+
+class
+octave_inline : public octave_class
+{
+public:
+
+  octave_inline (const octave_map& m)
+    : octave_class (m, "inline"), m_fcn_obj (new octave_inline_fcn (this))
+  { }
+
+  octave_inline (const octave_inline&) = default;
+
+  ~octave_inline (void) = default;
+
+  octave_base_value * clone (void) const { return new octave_inline (*this); }
+
+  octave_base_value * empty_clone (void) const
+  {
+    return new octave_inline (octave_map (map_keys ()));
+  }
+
+  bool is_inline_function (void) const { return true; }
+
+  octave_function * function_value (bool)
+  {
+    return m_fcn_obj.get ();
+  }
+
+private:
+
+  std::shared_ptr<octave_inline_fcn> m_fcn_obj;
+};
+
+octave_value_list
+octave_inline_fcn::call (octave::tree_evaluator& tw, int nargout,
+                         const octave_value_list& args)
+{
+  octave::interpreter& interp = tw.get_interpreter ();
+
+  return interp.feval (octave_value (m_inline_obj, true), args, nargout);
+}
+
+
+DEFUN (__inline_ctor__, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn {} {} __inline_ctor__ (@var{prop_struct})
+Internal function.
+
+Implements final construction for inline objects.
+@seealso{inline}
+@end deftypefn */)
+{
+  // Input validation has already been done in input.m.
+
+  return octave_value (new octave_inline (args(0).map_value ()));
+}
--- a/libinterp/octave-value/ov-class.h	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/ov-class.h	Mon Mar 23 15:29:49 2020 -0400
@@ -59,6 +59,11 @@
       parent_list (), obsolete_copies (0)
   { }
 
+  octave_class (const octave_map& m, const std::string& id)
+    : octave_base_value (), map (m), c_name (id),
+      parent_list (), obsolete_copies (0)
+  { }
+
   octave_class (const octave_map& m, const std::string& id,
                 const std::list<std::string>& plist)
     : octave_base_value (), map (m), c_name (id),
--- a/libinterp/octave-value/ov-fcn-handle.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -2052,12 +2052,10 @@
 }
 
 /*
-%!shared fh, finline
+%!shared fh
 %! fh = @(x) x;
-%! finline = inline ("x");
 
 %!assert (is_function_handle (fh))
-%!assert (is_function_handle (finline))
 %!assert (! is_function_handle ({fh}))
 %!assert (! is_function_handle (1))
 
--- a/libinterp/octave-value/ov-fcn-inline.cc	Sat Apr 25 13:17:11 2020 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1003 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2004-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-// In addition to the terms of the GPL, you are permitted to link
-// this program with any Open Source program, as defined by the
-// Open Source Initiative (www.opensource.org)
-//
-////////////////////////////////////////////////////////////////////////
-
-#if defined (HAVE_CONFIG_H)
-#  include "config.h"
-#endif
-
-#include <istream>
-#include <ostream>
-#include <sstream>
-#include <vector>
-
-#include "oct-locbuf.h"
-
-#include "defun.h"
-#include "error.h"
-#include "errwarn.h"
-#include "interpreter-private.h"
-#include "interpreter.h"
-#include "oct-hdf5.h"
-#include "oct-map.h"
-#include "ov-base.h"
-#include "ov-fcn-inline.h"
-#include "ov-usr-fcn.h"
-#include "parse.h"
-#include "pr-output.h"
-#include "variables.h"
-
-#include "byte-swap.h"
-#include "ls-ascii-helper.h"
-#include "ls-oct-text.h"
-#include "ls-hdf5.h"
-#include "ls-utils.h"
-
-
-DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_fcn_inline,
-                                     "inline function",
-                                     "function_handle");
-
-octave_fcn_inline::octave_fcn_inline (const std::string& f,
-                                      const string_vector& a,
-                                      const std::string& n)
-  : octave_fcn_handle (n), m_text (f), m_args (a)
-{
-  // Form a string representing the function.
-
-  std::ostringstream buf;
-
-  buf << "@(";
-
-  for (int i = 0; i < m_args.numel (); i++)
-    {
-      if (i > 0)
-        buf << ", ";
-
-      buf << m_args(i);
-    }
-
-  buf << ") " << m_text;
-
-  octave::interpreter& interp
-    = octave::__get_interpreter__ ("octave_fcn_inline::octave_fcn_inline");
-
-  int parse_status;
-  octave_value anon_fcn_handle
-    = interp.eval_string (buf.str (), true, parse_status);
-
-  if (parse_status == 0)
-    {
-      octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value ();
-
-      if (fh)
-        {
-          m_fcn = fh->fcn_val ();
-
-          octave_user_function *uf = m_fcn.user_function_value ();
-
-          if (uf)
-            {
-              octave::tree_evaluator& tw = interp.get_evaluator ();
-
-              octave_function *curr_fcn = tw.current_function ();
-
-              if (curr_fcn)
-                {
-                  octave::symbol_scope parent_scope
-                    = curr_fcn->parent_fcn_scope ();
-
-                  if (! parent_scope)
-                    parent_scope = curr_fcn->scope ();
-
-                  uf->stash_parent_fcn_scope (parent_scope);
-                }
-            }
-        }
-    }
-
-  if (m_fcn.is_undefined ())
-    error ("inline: unable to define function");
-}
-
-// This function is supplied to allow a Matlab style class structure
-// to be returned..
-octave_map
-octave_fcn_inline::map_value (void) const
-{
-  octave_scalar_map m;
-
-  m.assign ("version", 1.0);
-  m.assign ("isEmpty", 0.0);
-  m.assign ("expr", fcn_text ());
-
-  string_vector args = fcn_arg_names ();
-
-  m.assign ("numArgs", args.numel ());
-  m.assign ("args", args);
-
-  std::ostringstream buf;
-
-  for (int i = 0; i < args.numel (); i++)
-    buf << args(i) << " = INLINE_INPUTS_{" << i + 1 << "}; ";
-
-  m.assign ("inputExpr", buf.str ());
-
-  return m;
-}
-
-bool
-octave_fcn_inline::save_ascii (std::ostream& os)
-{
-  os << "# nargs: " <<  m_args.numel () << "\n";
-  for (int i = 0; i < m_args.numel (); i++)
-    os << m_args(i) << "\n";
-  if (m_name.length () < 1)
-    // Write an invalid value to flag empty fcn handle name.
-    os << "0\n";
-  else
-    os << m_name << "\n";
-  os << m_text << "\n";
-  return true;
-}
-
-bool
-octave_fcn_inline::load_ascii (std::istream& is)
-{
-  int nargs;
-  if (extract_keyword (is, "nargs", nargs, true))
-    {
-      m_args.resize (nargs);
-      for (int i = 0; i < nargs; i++)
-        is >> m_args(i);
-      is >> m_name;
-      if (m_name == "0")
-        m_name = "";
-
-      skip_preceeding_newline (is);
-
-      std::string buf;
-
-      if (is)
-        {
-
-          // Get a line of text whitespace characters included,
-          // leaving newline in the stream.
-          buf = read_until_newline (is, true);
-        }
-
-      m_text = buf;
-
-      octave_fcn_inline tmp (m_text, m_args, m_name);
-      m_fcn = tmp.m_fcn;
-
-      return true;
-    }
-  else
-    return false;
-}
-
-bool
-octave_fcn_inline::save_binary (std::ostream& os, bool)
-{
-  int32_t tmp = m_args.numel ();
-  os.write (reinterpret_cast<char *> (&tmp), 4);
-  for (int i = 0; i < m_args.numel (); i++)
-    {
-      tmp = m_args(i).length ();
-      os.write (reinterpret_cast<char *> (&tmp), 4);
-      os.write (m_args(i).c_str (), m_args(i).length ());
-    }
-  tmp = m_name.length ();
-  os.write (reinterpret_cast<char *> (&tmp), 4);
-  os.write (m_name.c_str (), m_name.length ());
-  tmp = m_text.length ();
-  os.write (reinterpret_cast<char *> (&tmp), 4);
-  os.write (m_text.c_str (), m_text.length ());
-  return true;
-}
-
-bool
-octave_fcn_inline::load_binary (std::istream& is, bool swap,
-                                octave::mach_info::float_format)
-{
-  int32_t nargs;
-  if (! is.read (reinterpret_cast<char *> (&nargs), 4))
-    return false;
-  if (swap)
-    swap_bytes<4> (&nargs);
-
-  if (nargs < 1)
-    return false;
-  else
-    {
-      int32_t tmp;
-      m_args.resize (nargs);
-      for (int i = 0; i < nargs; i++)
-        {
-          if (! is.read (reinterpret_cast<char *> (&tmp), 4))
-            return false;
-          if (swap)
-            swap_bytes<4> (&tmp);
-
-          OCTAVE_LOCAL_BUFFER (char, ctmp, tmp+1);
-          is.read (ctmp, tmp);
-          m_args(i) = std::string (ctmp);
-
-          if (! is)
-            return false;
-        }
-
-      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
-        return false;
-      if (swap)
-        swap_bytes<4> (&tmp);
-
-      OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1);
-      is.read (ctmp1, tmp);
-      m_name = std::string (ctmp1);
-
-      if (! is)
-        return false;
-
-      if (! is.read (reinterpret_cast<char *> (&tmp), 4))
-        return false;
-      if (swap)
-        swap_bytes<4> (&tmp);
-
-      OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
-      is.read (ctmp2, tmp);
-      m_text = std::string (ctmp2);
-
-      if (! is)
-        return false;
-
-      octave_fcn_inline ftmp (m_text, m_args, m_name);
-      m_fcn = ftmp.m_fcn;
-    }
-  return true;
-}
-
-bool
-octave_fcn_inline::save_hdf5 (octave_hdf5_id loc_id, const char *name,
-                              bool /* save_as_floats */)
-{
-  bool retval = false;
-
-#if defined (HAVE_HDF5)
-
-  hid_t group_hid = -1;
-
-#if defined (HAVE_HDF5_18)
-  group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
-                         octave_H5P_DEFAULT);
-#else
-  group_hid = H5Gcreate (loc_id, name, 0);
-#endif
-  if (group_hid < 0) return false;
-
-  size_t len = 0;
-  for (int i = 0; i < m_args.numel (); i++)
-    if (len < m_args(i).length ())
-      len = m_args(i).length ();
-
-  hid_t space_hid, data_hid, type_hid;
-  space_hid = data_hid = type_hid = -1;
-
-  // FIXME: Is there a better way of saving string vectors,
-  //        than a null padded matrix?
-
-  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
-
-  // Octave uses column-major, while HDF5 uses row-major ordering
-  hdims[1] = m_args.numel ();
-  hdims[0] = len + 1;
-
-  space_hid = H5Screate_simple (2, hdims, nullptr);
-  if (space_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid,
-                        octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dcreate (group_hid, "args", H5T_NATIVE_CHAR, space_hid,
-                        octave_H5P_DEFAULT);
-#endif
-  if (data_hid < 0)
-    {
-      H5Sclose (space_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (char, s, m_args.numel () * (len + 1));
-
-  // Save the args as a null terminated list
-  for (int i = 0; i < m_args.numel (); i++)
-    {
-      const char *cptr = m_args(i).c_str ();
-      for (size_t j = 0; j < m_args(i).length (); j++)
-        s[i*(len+1)+j] = *cptr++;
-      s[m_args(i).length ()] = '\0';
-    }
-
-  retval = H5Dwrite (data_hid, H5T_NATIVE_CHAR, octave_H5S_ALL, octave_H5S_ALL,
-                     octave_H5P_DEFAULT, s) >= 0;
-
-  H5Dclose (data_hid);
-  H5Sclose (space_hid);
-
-  if (! retval)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  // attach the type of the variable
-  type_hid = H5Tcopy (H5T_C_S1);
-  H5Tset_size (type_hid, m_name.length () + 1);
-  if (type_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  hdims[0] = 0;
-  space_hid = H5Screate_simple (0, hdims, nullptr);
-  if (space_hid < 0)
-    {
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
-                        octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dcreate (group_hid, "nm",  type_hid, space_hid,
-                        octave_H5P_DEFAULT);
-#endif
-  if (data_hid < 0
-      || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
-                   octave_H5P_DEFAULT, m_name.c_str ()) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-  H5Dclose (data_hid);
-
-  // attach the type of the variable
-  H5Tset_size (type_hid, m_text.length () + 1);
-  if (type_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dcreate (group_hid, "iftext",  type_hid, space_hid,
-                        octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dcreate (group_hid, "iftext",  type_hid, space_hid,
-                        octave_H5P_DEFAULT);
-#endif
-  if (data_hid < 0
-      || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
-                   octave_H5P_DEFAULT, m_text.c_str ()) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  H5Dclose (data_hid);
-  H5Sclose (space_hid);
-  H5Tclose (type_hid);
-  H5Gclose (group_hid);
-
-#else
-  octave_unused_parameter (loc_id);
-  octave_unused_parameter (name);
-
-  warn_save ("hdf5");
-#endif
-
-  return retval;
-}
-
-bool
-octave_fcn_inline::load_hdf5 (octave_hdf5_id loc_id, const char *name)
-{
-#if defined (HAVE_HDF5)
-
-  hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id;
-  hsize_t rank;
-  int slen;
-
-#if defined (HAVE_HDF5_18)
-  group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
-#else
-  group_hid = H5Gopen (loc_id, name);
-#endif
-  if (group_hid < 0) return false;
-
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dopen (group_hid, "args");
-#endif
-  space_hid = H5Dget_space (data_hid);
-  rank = H5Sget_simple_extent_ndims (space_hid);
-
-  if (rank != 2)
-    {
-      H5Dclose (data_hid);
-      H5Sclose (space_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
-  OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
-
-  H5Sget_simple_extent_dims (space_hid, hdims, maxdims);
-
-  m_args.resize (hdims[1]);
-
-  OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]);
-
-  if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL,
-               octave_H5P_DEFAULT, s1) < 0)
-    {
-      H5Dclose (data_hid);
-      H5Sclose (space_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  H5Dclose (data_hid);
-  H5Sclose (space_hid);
-
-  for (size_t i = 0; i < hdims[1]; i++)
-    m_args(i) = std::string (s1 + i*hdims[0]);
-
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dopen (group_hid, "nm");
-#endif
-
-  if (data_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  type_hid = H5Dget_type (data_hid);
-  type_class_hid = H5Tget_class (type_hid);
-
-  if (type_class_hid != H5T_STRING)
-    {
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  space_hid = H5Dget_space (data_hid);
-  rank = H5Sget_simple_extent_ndims (space_hid);
-
-  if (rank != 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  slen = H5Tget_size (type_hid);
-  if (slen < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
-
-  // create datatype for (null-terminated) string to read into:
-  st_id = H5Tcopy (H5T_C_S1);
-  H5Tset_size (st_id, slen);
-
-  if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
-               octave_H5P_DEFAULT, nm_tmp) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-  H5Tclose (st_id);
-  H5Dclose (data_hid);
-  m_name = nm_tmp;
-
-#if defined (HAVE_HDF5_18)
-  data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT);
-#else
-  data_hid = H5Dopen (group_hid, "iftext");
-#endif
-
-  if (data_hid < 0)
-    {
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  type_hid = H5Dget_type (data_hid);
-  type_class_hid = H5Tget_class (type_hid);
-
-  if (type_class_hid != H5T_STRING)
-    {
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  space_hid = H5Dget_space (data_hid);
-  rank = H5Sget_simple_extent_ndims (space_hid);
-
-  if (rank != 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  slen = H5Tget_size (type_hid);
-  if (slen < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Dclose (data_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-
-  OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen);
-
-  // create datatype for (null-terminated) string to read into:
-  st_id = H5Tcopy (H5T_C_S1);
-  H5Tset_size (st_id, slen);
-
-  if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
-               octave_H5P_DEFAULT, iftext_tmp) < 0)
-    {
-      H5Sclose (space_hid);
-      H5Tclose (type_hid);
-      H5Gclose (group_hid);
-      return false;
-    }
-  H5Tclose (st_id);
-  H5Dclose (data_hid);
-  m_text = iftext_tmp;
-
-  octave_fcn_inline ftmp (m_text, m_args, m_name);
-  m_fcn = ftmp.m_fcn;
-
-  return true;
-
-#else
-  octave_unused_parameter (loc_id);
-  octave_unused_parameter (name);
-
-  warn_load ("hdf5");
-
-  return false;
-#endif
-}
-
-void
-octave_fcn_inline::print (std::ostream& os, bool pr_as_read_syntax)
-{
-  print_raw (os, pr_as_read_syntax);
-  newline (os);
-}
-
-void
-octave_fcn_inline::print_raw (std::ostream& os, bool pr_as_read_syntax) const
-{
-  std::ostringstream buf;
-
-  if (m_name.empty ())
-    buf << "f(";
-  else
-    buf << m_name << '(';
-
-  for (int i = 0; i < m_args.numel (); i++)
-    {
-      if (i)
-        buf << ", ";
-
-      buf << m_args(i);
-    }
-
-  buf << ") = " << m_text;
-
-  octave_print_internal (os, buf.str (), pr_as_read_syntax,
-                         current_print_indent_level ());
-}
-
-octave_value
-octave_fcn_inline::convert_to_str_internal (bool, bool, char type) const
-{
-  return octave_value (fcn_text (), type);
-}
-
-DEFUNX ("inline", Finline, args, ,
-        doc: /* -*- texinfo -*-
-@deftypefn  {} {} inline (@var{str})
-@deftypefnx {} {} inline (@var{str}, @var{arg1}, @dots{})
-@deftypefnx {} {} inline (@var{str}, @var{n})
-Create an inline function from the character string @var{str}.
-
-If called with a single argument, the arguments of the generated function
-are extracted from the function itself.  The generated function arguments
-will then be in alphabetical order.  It should be noted that i and j are
-ignored as arguments due to the ambiguity between their use as a variable or
-their use as an built-in constant.  All arguments followed by a parenthesis
-are considered to be functions.  If no arguments are found, a function
-taking a single argument named @code{x} will be created.
-
-If the second and subsequent arguments are character strings, they are the
-names of the arguments of the function.
-
-If the second argument is an integer @var{n}, the arguments are
-@qcode{"x"}, @qcode{"P1"}, @dots{}, @qcode{"P@var{N}"}.
-
-Programming Note: The use of @code{inline} is discouraged and it may be
-removed from a future version of Octave.  The preferred way to create
-functions from strings is through the use of anonymous functions
-(@pxref{Anonymous Functions}) or @code{str2func}.
-@seealso{argnames, formula, vectorize, str2func}
-@end deftypefn */)
-{
-  int nargin = args.length ();
-
-  if (nargin == 0)
-    print_usage ();
-
-  std::string fun = args(0).xstring_value ("inline: STR argument must be a string");
-
-  string_vector fargs;
-
-  if (nargin == 1)
-    {
-      bool is_arg = false;
-      bool in_string = false;
-      std::string tmp_arg;
-      size_t i = 0;
-      size_t fun_length = fun.length ();
-
-      while (i < fun_length)
-        {
-          bool terminate_arg = false;
-          char c = fun[i++];
-
-          if (in_string)
-            {
-              if (c == '\'' || c == '\"')
-                in_string = false;
-            }
-          else if (c == '\'' || c == '\"')
-            {
-              in_string = true;
-              if (is_arg)
-                terminate_arg = true;
-            }
-          else if (! isalpha (c) && c != '_')
-            if (! is_arg)
-              continue;
-            else if (isdigit (c))
-              tmp_arg.append (1, c);
-            else
-              {
-                // Before we do anything remove trailing whitespaces.
-                while (i < fun_length && isspace (c))
-                  c = fun[i++];
-
-                // Do we have a variable or a function?
-                if (c != '(')
-                  terminate_arg = true;
-                else
-                  {
-                    tmp_arg = "";
-                    is_arg = false;
-                  }
-              }
-          else if (! is_arg)
-            {
-              if (c == 'e' || c == 'E')
-                {
-                  // possible number in exponent form, not arg
-                  if (isdigit (fun[i]) || fun[i] == '-' || fun[i] == '+')
-                    continue;
-                }
-              is_arg = true;
-              tmp_arg.append (1, c);
-            }
-          else
-            {
-              tmp_arg.append (1, c);
-            }
-
-          if (terminate_arg || (i == fun_length && is_arg))
-            {
-              bool have_arg = false;
-
-              for (int j = 0; j < fargs.numel (); j++)
-                if (tmp_arg == fargs (j))
-                  {
-                    have_arg = true;
-                    break;
-                  }
-
-              if (! have_arg && tmp_arg != "i" && tmp_arg != "j"
-                  && tmp_arg != "NaN" && tmp_arg != "nan"
-                  && tmp_arg != "Inf" && tmp_arg != "inf"
-                  && tmp_arg != "NA" && tmp_arg != "pi"
-                  && tmp_arg != "e" && tmp_arg != "eps")
-                fargs.append (tmp_arg);
-
-              tmp_arg = "";
-              is_arg = false;
-            }
-        }
-
-      // Sort the arguments into ASCII order.
-      fargs.sort ();
-
-      if (fargs.isempty ())
-        fargs.append (std::string ("x"));
-
-    }
-  else if (nargin == 2 && args(1).isnumeric ())
-    {
-      if (! args(1).is_scalar_type ())
-        error ("inline: N must be an integer");
-
-      int n = args(1).xint_value ("inline: N must be an integer");
-
-      if (n < 0)
-        error ("inline: N must be a positive integer or zero");
-
-      fargs.resize (n+1);
-
-      fargs(0) = "x";
-
-      for (int i = 1; i < n+1; i++)
-        {
-          std::ostringstream buf;
-          buf << 'P' << i;
-          fargs(i) = buf.str ();
-        }
-    }
-  else
-    {
-      fargs.resize (nargin - 1);
-
-      for (int i = 1; i < nargin; i++)
-        {
-          std::string s = args(i).xstring_value ("inline: additional arguments must be strings");
-          fargs(i-1) = s;
-        }
-    }
-
-  return ovl (new octave_fcn_inline (fun, fargs));
-}
-
-/*
-%!shared fn
-%! fn = inline ("x.^2 + 1");
-%!assert (feval (fn, 6), 37)
-%!assert (fn (6), 37)
-%!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10)
-%!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5)
-%!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5)
-
-## Test input validation
-%!error inline ()
-%!error <STR argument must be a string> inline (1)
-%!error <N must be an integer> inline ("2", ones (2,2))
-%!error <N must be a positive integer> inline ("2", -1)
-%!error <additional arguments must be strings> inline ("2", "x", -1, "y")
-*/
-
-DEFUN (formula, args, ,
-       doc: /* -*- texinfo -*-
-@deftypefn {} {} formula (@var{fun})
-Return a character string representing the inline function @var{fun}.
-
-Note that @code{char (@var{fun})} is equivalent to
-@code{formula (@var{fun})}.
-@seealso{char, argnames, inline, vectorize}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  octave_fcn_inline *fn = args(0).fcn_inline_value (true);
-
-  if (! fn)
-    error ("formula: FUN must be an inline function");
-
-  return ovl (fn->fcn_text ());
-}
-
-/*
-%!assert (formula (fn), "x.^2 + 1")
-%!assert (formula (fn), char (fn))
-
-## Test input validation
-%!error formula ()
-%!error formula (1, 2)
-%!error <FUN must be an inline function> formula (1)
-*/
-
-DEFUN (argnames, args, ,
-       doc: /* -*- texinfo -*-
-@deftypefn {} {} argnames (@var{fun})
-Return a cell array of character strings containing the names of the
-arguments of the inline function @var{fun}.
-@seealso{inline, formula, vectorize}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  octave_fcn_inline *fn = args(0).fcn_inline_value (true);
-
-  if (! fn)
-    error ("argnames: FUN must be an inline function");
-
-  string_vector t1 = fn->fcn_arg_names ();
-
-  Cell t2 (dim_vector (t1.numel (), 1));
-
-  for (int i = 0; i < t1.numel (); i++)
-    t2(i) = t1(i);
-
-  return ovl (t2);
-}
-
-/*
-%!assert (argnames (fn), {"x"})
-%!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"})
-%!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"})
-
-## Test input validation
-%!error argnames ()
-%!error argnames (1, 2)
-%!error <FUN must be an inline function> argnames (1)
-*/
-
-DEFUN (vectorize, args, ,
-       doc: /* -*- texinfo -*-
-@deftypefn {} {} vectorize (@var{fun})
-Create a vectorized version of the inline function @var{fun} by replacing
-all occurrences of @code{*}, @code{/}, etc., with @code{.*}, @code{./}, etc.
-
-This may be useful, for example, when using inline functions with numerical
-integration or optimization where a vector-valued function is expected.
-
-@example
-@group
-fcn = vectorize (inline ("x^2 - 1"))
-   @result{} fcn = f(x) = x.^2 - 1
-quadv (fcn, 0, 3)
-   @result{} 6
-@end group
-@end example
-@seealso{inline, formula, argnames}
-@end deftypefn */)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  std::string old_func;
-  octave_fcn_inline *old = nullptr;
-  bool func_is_string = true;
-
-  if (args(0).is_string ())
-    old_func = args(0).string_value ();
-  else
-    {
-      func_is_string = false;
-
-      old = args(0).fcn_inline_value (true);
-      if (! old)
-        error ("vectorize: FUN must be a string or inline function");
-
-      old_func = old->fcn_text ();
-    }
-
-  size_t len = old_func.length ();
-  std::string new_func;
-  new_func.reserve (len + 10);
-
-  size_t i = 0;
-  while (i < len)
-    {
-      char t1 = old_func[i];
-
-      if (t1 == '*' || t1 == '/' || t1 == '\\' || t1 == '^')
-        {
-          if (i && old_func[i-1] != '.')
-            new_func.push_back ('.');
-
-          // Special case for ** operator.
-          if (t1 == '*' && i < (len - 1) && old_func[i+1] == '*')
-            {
-              new_func.push_back ('*');
-              i++;
-            }
-        }
-      new_func.push_back (t1);
-      i++;
-    }
-
-  if (func_is_string)
-    return ovl (new_func);
-  else
-    return ovl (new octave_fcn_inline (new_func, old->fcn_arg_names ()));
-}
-
-/*
-%!assert (char (vectorize (fn)), "x.^2 + 1")
-%!assert (char (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z")
-%!assert (char (vectorize (inline ("2**x^5"))), "2.**x.^5")
-%!assert (vectorize ("x.^2 + 1"), "x.^2 + 1")
-%!assert (vectorize ("1e-3*y + 2e4*z"), "1e-3.*y + 2e4.*z")
-%!assert (vectorize ("2**x^5"), "2.**x.^5")
-
-## Test input validation
-%!error vectorize ()
-%!error vectorize (1, 2)
-%!error <FUN must be a string or inline function> vectorize (1)
-*/
--- a/libinterp/octave-value/ov-fcn-inline.h	Sat Apr 25 13:17:11 2020 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2004-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-#if ! defined (octave_ov_fcn_inline_h)
-#define octave_ov_fcn_inline_h 1
-
-#include "octave-config.h"
-
-#include <iosfwd>
-#include <string>
-
-#include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-fcn.h"
-#include "ov-typeinfo.h"
-#include "ov-fcn-handle.h"
-
-// Inline functions.
-
-class
-OCTINTERP_API
-octave_fcn_inline : public octave_fcn_handle
-{
-public:
-
-  octave_fcn_inline (void)
-    : octave_fcn_handle (), m_text (), m_args () { }
-
-  octave_fcn_inline (const std::string& f, const string_vector& a,
-                     const std::string& n = "");
-
-  octave_fcn_inline (const octave_fcn_inline& fi)
-    : octave_fcn_handle (fi), m_text (fi.m_text), m_args (fi.m_args) { }
-
-  ~octave_fcn_inline (void) = default;
-
-  octave_base_value * clone (void) const
-  { return new octave_fcn_inline (*this); }
-  octave_base_value * empty_clone (void) const
-  { return new octave_fcn_inline (); }
-
-  bool is_inline_function (void) const { return true; }
-
-  octave_fcn_inline * fcn_inline_value (bool = false) { return this; }
-
-  std::string fcn_text (void) const { return m_text; }
-
-  string_vector fcn_arg_names (void) const { return m_args; }
-
-  octave_value convert_to_str_internal (bool, bool, char) const;
-
-  octave_map map_value (void) const;
-
-  bool save_ascii (std::ostream& os);
-
-  bool load_ascii (std::istream& is);
-
-  bool save_binary (std::ostream& os, bool save_as_floats);
-
-  bool load_binary (std::istream& is, bool swap,
-                    octave::mach_info::float_format fmt);
-
-  bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
-
-  bool load_hdf5 (octave_hdf5_id loc_id, const char *name);
-
-  void print (std::ostream& os, bool pr_as_read_syntax = false);
-
-  void print_raw (std::ostream& os, bool pr_as_read_syntax = false) const;
-
-private:
-
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
-
-  // The expression of an inline function.
-  std::string m_text;
-
-  // The args of an inline function.
-  string_vector m_args;
-};
-
-#endif
--- a/libinterp/octave-value/ov-typeinfo.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/ov-typeinfo.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -991,8 +991,6 @@
 %!assert (typeinfo (@sin), "function handle")
 %!assert (typeinfo (@(x) x), "function handle")
 
-%!assert (typeinfo (inline ("x^2")), "inline function")
-
 %!assert (typeinfo (single (1)), "float scalar")
 %!assert (typeinfo (single (i)), "float complex scalar")
 %!assert (typeinfo (single ([1, 2])), "float matrix")
--- a/libinterp/octave-value/ov.cc	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/ov.cc	Mon Mar 23 15:29:49 2020 -0400
@@ -75,7 +75,6 @@
 #include "ov-dld-fcn.h"
 #include "ov-usr-fcn.h"
 #include "ov-fcn-handle.h"
-#include "ov-fcn-inline.h"
 #include "ov-typeinfo.h"
 #include "ov-null-mat.h"
 #include "ov-lazy-idx.h"
@@ -1694,12 +1693,6 @@
   return rep->fcn_handle_value (silent);
 }
 
-octave_fcn_inline *
-octave_value::fcn_inline_value (bool silent) const
-{
-  return rep->fcn_inline_value (silent);
-}
-
 octave_value_list
 octave_value::list_value (void) const
 {
@@ -2119,7 +2112,6 @@
 XVALUE_EXTRACTOR (octave_user_script *, xuser_script_value, user_script_value)
 XVALUE_EXTRACTOR (octave_user_code *, xuser_code_value, user_code_value)
 XVALUE_EXTRACTOR (octave_fcn_handle *, xfcn_handle_value, fcn_handle_value)
-XVALUE_EXTRACTOR (octave_fcn_inline *, xfcn_inline_value, fcn_inline_value)
 
 XVALUE_EXTRACTOR (octave_value_list, xlist_value, list_value)
 
@@ -2962,7 +2954,6 @@
   octave_user_function::register_type (ti);
   octave_dld_function::register_type (ti);
   octave_fcn_handle::register_type (ti);
-  octave_fcn_inline::register_type (ti);
   octave_float_scalar::register_type (ti);
   octave_float_complex::register_type (ti);
   octave_float_matrix::register_type (ti);
--- a/libinterp/octave-value/ov.h	Sat Apr 25 13:17:11 2020 -0400
+++ b/libinterp/octave-value/ov.h	Mon Mar 23 15:29:49 2020 -0400
@@ -57,7 +57,6 @@
 class octave_function;
 class octave_user_function;
 class octave_fcn_handle;
-class octave_fcn_inline;
 class octave_value_list;
 
 #include "oct-stream.h"
@@ -974,8 +973,6 @@
 
   octave_fcn_handle * fcn_handle_value (bool silent = false) const;
 
-  octave_fcn_inline * fcn_inline_value (bool silent = false) const;
-
   octave_value_list list_value (void) const;
 
   ColumnVector column_vector_value (bool frc_str_conv = false,
@@ -1193,7 +1190,6 @@
   octave_user_script * xuser_script_value (const char *fmt, ...) const;
   octave_user_code * xuser_code_value (const char *fmt, ...) const;
   octave_fcn_handle * xfcn_handle_value (const char *fmt, ...) const;
-  octave_fcn_inline * xfcn_inline_value (const char *fmt, ...) const;
 
   octave_value_list xlist_value (const char *fmt, ...) const;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/argnames.m	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,16 @@
+## -*- texinfo -*-
+## @deftypefn {} {} argnames (@var{fun})
+## Return a cell array of character strings containing the names of the
+## arguments of the inline function @var{fun}.
+## @seealso{inline, formula, vectorize}
+## @end deftypefn
+
+function args = argnames (obj)
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  args = obj.args;
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/char.m	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,18 @@
+## -*- texinfo -*-
+## @deftypefn {} {} char (@var{fun})
+## Return a character string representing the inline function @var{fun}.
+##
+## Note that @code{char (@var{fun})} is equivalent to
+## @code{formula (@var{fun})}.
+## @seealso{char, argnames, inline, vectorize}
+## @end deftypefn
+
+function expr = char (obj)
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  expr = obj.expr;
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/feval.m	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,36 @@
+########################################################################
+##
+## Copyright (C) 2020 The Octave Project Developers
+##
+## See the file COPYRIGHT.md in the top-level directory of this
+## distribution or <https://octave.org/copyright/>.
+##
+## This file is part of Octave.
+##
+## Octave is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+##
+########################################################################
+
+function retval = feval (fcn, varargin)
+
+  if (nargin < 1)
+    print_usage ();
+  endif
+
+  fh = eval (sprintf ("@(%s) %s", strjoin (fcn.args, ","), fcn.expr));
+
+  retval = fh (varargin{:});
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/formula.m	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,18 @@
+## -*- texinfo -*-
+## @deftypefn {} {} formula (@var{fun})
+## Return a character string representing the inline function @var{fun}.
+##
+## Note that @code{char (@var{fun})} is equivalent to
+## @code{formula (@var{fun})}.
+## @seealso{char, argnames, inline, vectorize}
+## @end deftypefn
+
+function expr = formula (obj)
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  expr = obj.expr;
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/inline.m	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,190 @@
+########################################################################
+##
+## Copyright (C) 2020 The Octave Project Developers
+##
+## See the file COPYRIGHT.md in the top-level directory of this
+## distribution or <https://octave.org/copyright/>.
+##
+## This file is part of Octave.
+##
+## Octave is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn  {} {} inline (@var{str})
+## @deftypefnx {} {} inline (@var{str}, @var{arg1}, @dots{})
+## @deftypefnx {} {} inline (@var{str}, @var{n})
+##
+## This function is obsolete.  Use anonymous functions
+## (@pxref{Anonymous Functions}) instead.
+##
+## Create an inline function from the character string @var{str}.
+##
+## If called with a single argument, the arguments of the generated
+## function are extracted from the function itself.  The generated
+## function arguments will then be in alphabetical order.  It should be
+## noted that i and j are ignored as arguments due to the ambiguity
+## between their use as a variable or their use as an built-in constant.
+## All arguments followed by a parenthesis are considered to be
+## functions.  If no arguments are found, a function taking a single
+## argument named @code{x} will be created.
+##
+## If the second and subsequent arguments are character strings, they
+## are the names of the arguments of the function.
+##
+## If the second argument is an integer @var{n}, the arguments are
+## @qcode{"x"}, @qcode{"P1"}, @dots{}, @qcode{"P@var{N}"}.
+##
+## @strong{Caution:} the use of @code{inline} is discouraged and it may
+## be removed from a future version of Octave.  The preferred way to
+## create functions from strings is through the use of anonymous
+## functions (@pxref{Anonymous Functions}) or @code{str2func}.
+## @seealso{argnames, formula, vectorize, str2func}
+## @end deftypefn
+
+function obj = inline (expr, varargin)
+
+  if (nargin == 0)
+    print_usage ();
+  endif
+
+  if (! ischar (expr))
+    error ("inline: EXPR must be a string");
+  endif
+
+  if (nargin == 1)
+    args = parse_expr_for_args (expr);
+  elseif (nargin == 2)
+    n = varargin{1};
+    if (isnumeric (n))
+      if (isscalar (n) && fix (n) == n)
+        if (n > 0)
+          args = strsplit (["x", sprintf(":P%d", 1:n)], ":");
+        else
+          error ("inline: N must be a positive integer");
+        endif
+      else
+        error ("inline: N must be an integer");
+      endif
+    else
+      args = {"x"};
+    endif
+  elseif (iscellstr (varargin))
+    args = varargin;
+  else
+    error ("inline: additional arguments must be strings");
+  endif
+
+  p.expr = expr;
+  p.args = args(:);
+  p.numArgs = numel (args);
+  tmp = [args; num2cell(1:numel(args))];
+  p.inputExpr = sprintf ("%s = INLINE_INPUTS_{%d}; ", tmp{:});
+  p.isEmpty = false;
+  p.version = 1;
+
+  obj = __inline_ctor__ (p);
+
+endfunction
+
+## The following function was translated directly from the original C++
+## version.  Yes, it will be slow, but the use of inline functions is
+## strongly discouraged anyway, and most expressions will probably be
+## short.  It may also be buggy.  Well, don't use this object!  Use
+## function handles instead!
+
+function args = parse_expr_for_args (expr)
+
+  persistent symbols_to_skip = {"i", "j", "NaN", "nan", "Inf", "inf", ...
+                                "NA", "pi", "e", "eps"};
+
+  is_arg = false;
+  in_string = false;
+  tmp_arg = "";
+  i = 1;
+  expr_length = length (expr);
+  args = {};
+
+  while (i <= expr_length)
+
+    terminate_arg = false;
+    c = expr(i++);
+
+    if (in_string)
+      if (c == "'" || c == '"')
+        in_string = false;
+      endif
+    elseif (c == "'" || c == '"')
+      in_string = true;
+      if (is_arg)
+        terminate_arg = true;
+      endif
+    elseif (! isalpha (c) && c != "_")
+      if (! is_arg)
+        continue;
+      elseif (isdigit (c))
+        tmp_arg(end+1) = c;
+      else
+        ## Before we do anything remove trailing whitespaces.
+        while (i <= expr_length && isspace (c))
+          c = expr(i++);
+        endwhile
+
+        ## Do we have a variable or a function?
+        if (c != "(")
+          terminate_arg = true;
+        else
+          tmp_arg = "";
+          is_arg = false;
+        endif
+      endif
+    elseif (! is_arg)
+      if (c == "e" || c == "E")
+        ## Possible number in exponent form, not arg.
+        if (isdigit (expr(i)) || expr(i) == "-" || expr(i) == "+")
+          continue;
+        endif
+      endif
+      is_arg = true;
+      tmp_arg(end+1) = c;
+    else
+      tmp_arg(end+1) = c;
+    endif
+
+    if (terminate_arg || (i == expr_length+1 && is_arg))
+      have_arg = false;
+      if (any (strcmp (tmp_arg, args)))
+        have_arg = true;
+      endif
+
+      if (! (have_arg || any (strcmp (tmp_arg, symbols_to_skip))))
+        args{end+1} = tmp_arg;
+      endif
+
+      tmp_arg = "";
+      is_arg = false;
+    endif
+
+  endwhile
+
+  ## Sort the arguments into ASCII order.
+  args = sort (args);
+
+  if (isempty (args))
+    args = {"x"};
+  endif
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/module.mk	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,11 @@
+## Automake fails to process "include %reldir%/module.mk" in the directory
+## above.  All of the commands which would normally be in this file were
+## manually placed in scripts/legacy/module.mk to avoid using the "include"
+## directive.
+##
+## This is an Automake bug.  Automake has switched to a Perl backend which uses
+## the following pattern to detect a path:
+##
+## my $PATH_PATTERN = '(\w|[+/.-])+';
+##
+## This pattern only includes alphanumeric, '_', and [+/.-], but not "@".
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/subsref.m	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,52 @@
+########################################################################
+##
+## Copyright (C) 2020 The Octave Project Developers
+##
+## See the file COPYRIGHT.md in the top-level directory of this
+## distribution or <https://octave.org/copyright/>.
+##
+## This file is part of Octave.
+##
+## Octave is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn {} {@var{value} =} subsref (@var{fcn}, @var{idx})
+## Perform subscripted function call on the inline function object @var{fcn}.
+## @end deftypefn
+
+function retval = subsref (fcn, idx)
+
+  if (nargin != 2)
+    print_usage ();
+  endif
+
+  if (isempty (idx))
+    error ("@inline/subsref: missing index");
+  endif
+
+  if (strcmp (idx(1).type, "()"))
+    args = idx.subs;
+    if (numel (args) > 0)
+      retval = feval (fcn, args{:});
+    else
+      retval = feval (fcn);
+    endif
+  else
+    error ("@inline/subsref: invalid subscript type");
+  endif
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/legacy/@inline/vectorize.m	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,62 @@
+## -*- texinfo -*-
+## @deftypefn {} {} vectorize (@var{fun})
+## Create a vectorized version of the inline function @var{fun} by
+## replacing all occurrences of @code{*}, @code{/}, etc., with
+## @code{.*}, @code{./}, etc.
+##
+## This may be useful, for example, when using inline functions with
+## numerical integration or optimization where a vector-valued function
+## is expected.
+##
+## @example
+## @group
+## fcn = vectorize (inline ("x^2 - 1"))
+##    @result{} fcn = f(x) = x.^2 - 1
+## quadv (fcn, 0, 3)
+##    @result{} 6
+## @end group
+## @end example
+## @seealso{inline, formula, argnames}
+## @end deftypefn
+
+## The following function was translated directly from the original C++
+## version.  Yes, it will be slow, but the use of inline functions is
+## strongly discouraged anyway, and most expressions will probably be
+## short.  It may also be buggy.  Well, don't use this object!  Use
+## function handles instead!
+
+function fcn = vectorize (obj)
+
+  if (nargin != 1)
+    print_usage ();
+  endif
+
+  new_expr = "";
+
+  expr = obj.expr;
+  len = length (expr);
+  i = 1;
+
+  while (i <= len)
+    c = expr(i);
+
+    if (c == "*" || c == "/" || c == "\\" || c == "^")
+      if (i > 1 && expr(i-1) != ".")
+        new_expr(end+1) = ".";
+      endif
+
+      ## Special case for ** operator.
+      if (c == '*' && i < (len - 1) && expr(i+1) == '*')
+        new_expr(end+1) = "*";
+        i++;
+      endif
+    endif
+
+    new_expr(end+1) = c;
+    i++;
+
+  endwhile
+
+  fcn = inline (new_expr);
+
+endfunction
--- a/scripts/legacy/module.mk	Sat Apr 25 13:17:11 2020 -0400
+++ b/scripts/legacy/module.mk	Mon Mar 23 15:29:49 2020 -0400
@@ -12,6 +12,31 @@
   %reldir%/strread.m \
   %reldir%/textread.m
 
+## include %reldir%/@inline/module.mk
+## The include above fails because Automake cannot process the '@' character.
+## As a work around, the contents of %reldir%/@inline/module.mk are placed directly
+## in this module.mk file.
+scripts_EXTRA_DIST += %reldir%/@inline/module.mk
+######################## include %reldir%/@inline/module.mk ########################
+FCN_FILE_DIRS += %reldir%/@inline
+
+%canon_reldir%_@inline_FCN_FILES = \
+  %reldir%/@inline/argnames.m \
+  %reldir%/@inline/formula.m \
+  %reldir%/@inline/inline.m \
+  %reldir%/@inline/vectorize.m
+
+%canon_reldir%_@inlinedir = $(fcnfiledir)/@inline
+
+%canon_reldir%_@inline_DATA = $(%canon_reldir%_@inline_FCN_FILES)
+
+FCN_FILES += $(%canon_reldir%_@inline_FCN_FILES)
+
+PKG_ADD_FILES += %reldir%/@inline/PKG_ADD
+
+DIRSTAMP_FILES += %reldir%/@inline/$(octave_dirstamp)
+####################### end include %reldir%/@inline/module.mk #####################
+
 %canon_reldir%dir = $(fcnfiledir)/legacy
 
 %canon_reldir%_DATA = $(%canon_reldir%_FCN_FILES)
--- a/scripts/module.mk	Sat Apr 25 13:17:11 2020 -0400
+++ b/scripts/module.mk	Mon Mar 23 15:29:49 2020 -0400
@@ -45,7 +45,7 @@
 ## The include above fails because Automake cannot process the '@' character.
 ## As a work around, the contents of %reldir%/@ftp/module.mk are placed directly
 ## in this module.mk file.
-%canon_reldir%_EXTRA_DIST += %reldir%/@ftp/module.mk
+scripts_EXTRA_DIST += %reldir%/@ftp/module.mk
 ######################## include %reldir%/@ftp/module.mk ########################
 FCN_FILE_DIRS += %reldir%/@ftp
 
--- a/scripts/plot/draw/fplot.m	Sat Apr 25 13:17:11 2020 -0400
+++ b/scripts/plot/draw/fplot.m	Mon Mar 23 15:29:49 2020 -0400
@@ -83,11 +83,9 @@
 ##
 ## @code{fplot} performance is better when the function accepts and returns a
 ## vector argument.  Consider this when writing user-defined functions and use
-## element-by-element operators such as @code{.*}, @code{./}, etc.  See the
-## function @code{vectorize} for potentially converting inline or anonymous
-## functions to vectorized versions.
+## element-by-element operators such as @code{.*}, @code{./}, etc.
 ##
-## @seealso{ezplot, plot, vectorize}
+## @seealso{ezplot, plot}
 ## @end deftypefn
 
 function [X, Y] = fplot (varargin)
@@ -99,8 +97,8 @@
   endif
 
   fn = varargin{1};
-  if (strcmp (typeinfo (fn), "inline function"))
-    fn = vectorize (fn);
+  if (isa (fn, "inline"))
+    fn = vectorize (inline (fn));
     nam = formula (fn);
   elseif (is_function_handle (fn))
     nam = func2str (fn);
--- a/scripts/plot/draw/private/__ezplot__.m	Sat Apr 25 13:17:11 2020 -0400
+++ b/scripts/plot/draw/private/__ezplot__.m	Mon Mar 23 15:29:49 2020 -0400
@@ -84,7 +84,7 @@
     endif
   endif
 
-  if (strcmp (typeinfo (fun), "inline function"))
+  if (isa (fun, "inline"))
     argids = argnames (fun);
     if (isplot && length (argids) == 2)
       nargs = 2;
@@ -147,7 +147,7 @@
       yarg = args{2};
     endif
   else
-    error ("%s: F must be string, inline function, or function handle", ezfunc);
+    error ("%s: F must be a string or function handle", ezfunc);
   endif
 
   if (nargin > 2 || (nargin == 2 && isplot))
@@ -165,7 +165,7 @@
         error ("%s: expecting a function of %d arguments", ezfunc, nargs);
       endif
       fstry = formula (funy);
-    elseif (strcmp (typeinfo (funy), "inline function"))
+    elseif (isa (funy, "inline"))
       parametric = true;
       if (numel (argnames (funy)) != nargs)
         error ("%s: expecting a function of %d arguments", ezfunc, nargs);
@@ -210,7 +210,7 @@
           error ("%s: expecting a function of %d arguments", ezfunc, nargs);
         endif
         fstrz = formula (funz);
-      elseif (strcmp (typeinfo (funz), "inline function"))
+      elseif (isa (funz, "inline"))
         if (numel (argnames (funz)) != nargs)
           error ("%s: expecting a function of %d arguments", ezfunc, nargs);
         endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/inline-fcn.tst	Mon Mar 23 15:29:49 2020 -0400
@@ -0,0 +1,33 @@
+## INLINE contstructor
+
+%!shared fn
+%! fn = inline ("x.^2 + 1");
+%!assert (feval (fn, 6), 37)
+%!assert (fn (6), 37)
+%!assert (feval (inline ("sum (x(:))"), [1 2; 3 4]), 10)
+%!assert (feval (inline ("sqrt (x^2 + y^2)", "x", "y"), 3, 4), 5)
+%!assert (feval (inline ("exp (P1*x) + P2", 3), 3, 4, 5), exp(3*4) + 5)
+
+## Test input validation
+%!error inline ()
+%!error <EXPR must be a string> inline (1)
+%!error <N must be an integer> inline ("2", ones (2,2))
+%!error <N must be a positive integer> inline ("2", -1)
+%!error <additional arguments must be strings> inline ("2", "x", -1, "y")
+
+## FORMULA
+
+%!assert (formula (fn), "x.^2 + 1")
+%!assert (formula (fn), char (fn))
+
+## ARGNAMES
+
+%!assert (argnames (fn), {"x"})
+%!assert (argnames (inline ("1e-3*y + 2e4*z")), {"y"; "z"})
+%!assert (argnames (inline ("2", 2)), {"x"; "P1"; "P2"})
+
+## VECTORIZE
+
+%!assert (formula (vectorize (fn)), "x.^2 + 1")
+%!assert (formula (vectorize (inline ("1e-3*y + 2e4*z"))), "1e-3.*y + 2e4.*z")
+%!assert (formula (vectorize (inline ("2**x^5"))), "2.**x.^5")
--- a/test/module.mk	Sat Apr 25 13:17:11 2020 -0400
+++ b/test/module.mk	Mon Mar 23 15:29:49 2020 -0400
@@ -31,6 +31,7 @@
   %reldir%/global.tst \
   %reldir%/if.tst \
   %reldir%/index.tst \
+  %reldir%/inline-fcn.tst \
   %reldir%/integer.tst \
   %reldir%/io.tst \
   %reldir%/jit.tst \