# HG changeset patch
# User Piotr Held <pjheld@gmail.com>
# Date 1504743404 25200
#      Wed Sep 06 17:16:44 2017 -0700
# Node ID e6c50355c5004853c05e269e27c5a46dfe4ade90
# Parent  9107bae20480c6dff75307a1462c6986f4cb00c3
Fixed problem with loading function handle to user function (Bug #47763)

* ov.h, ov-base.h, ov-usr-fcn.h, ov-usr-fcn.cc: added property dummy_function which holds whether a user function is a dummy function. If it is a dummy function then a octave_fcn_handle that contains it will always run it lazyily (looking it's name up). This property is accessible from octave_value::is_dummy_function ().
* ov-fcn-handle.h, ov-fcn-handle.cc: expanded the constructor that accepts only name as input to associate a dummy function with the octave_fcn_handle. Also modified the subsref to just evaluate a dummy function using its name.
* ov-fcn-handle.cc (make_fcn_handle): instead of an error message when no function is found make_fcn_handle creates a lazy octave_fcn_handle with a dummy function.
* ls-mat5.cc (manage_fcn_handle_creation): querries load_path to check if function exists at the function handle location. If it does not, then it searches the path for function of the same name. If that fails it creates a lazy function handle that references a dummy_function.
* test/semi-lazy-fcn-handle/*: added test for lazy function handle creation.

diff -r 9107bae20480 -r e6c50355c500 libinterp/corefcn/ls-mat5.cc
--- a/libinterp/corefcn/ls-mat5.cc	Wed Sep 06 09:25:46 2017 -0400
+++ b/libinterp/corefcn/ls-mat5.cc	Wed Sep 06 17:16:44 2017 -0700
@@ -116,6 +116,58 @@
   MAT_FILE_WORKSPACE_CLASS            // Workspace (undocumented)
 };
 
+// Helper function to properly create function handles
+// Checks if dir_name exists in load path
+// if it does then load that function.
+// If not then create function handle purely
+// based on name using make_fcn_handle ()
+
+octave_value
+manage_fcn_handle_creation (const std::string& fpath,
+                            const std::string& fname)
+{
+  octave_value retval;
+
+  size_t xpos
+    = fpath.find_last_of (octave::sys::file_ops::dir_sep_chars ());
+
+  std::string dir_name = fpath.substr (0, xpos);
+
+  octave::interpreter * interp
+    = octave::interpreter::the_interpreter ();
+  octave::load_path& lp = interp->get_load_path ();
+
+  // Check if function exists in path
+  // Cannot use lp.find_fcn () because it does not look in
+  // the dir_name folder first
+  bool fpath_file_exist = false;
+  std::list<std::string> dirs = lp.dir_list ();
+  if (std::find(dirs.begin (), dirs.end (), dir_name)
+      != dirs.end())
+    {
+      string_vector files = lp.files (dir_name, true);
+      for (octave_idx_type i = 0; i < files.numel (); i++)
+        {
+          if (files (i) == fname)
+            {
+              fpath_file_exist = true;
+              break;
+            }
+        } 
+    }
+
+  if (fpath_file_exist)
+    {
+      octave_value ov_fcn
+        = octave::load_fcn_from_file (fpath, dir_name, "", "", fname);
+      retval = octave_value (new octave_fcn_handle (ov_fcn, fname));
+    }
+  else
+    retval = make_fcn_handle (fname);
+
+  return retval;
+}
+
 // Read COUNT elements of data from IS in the format specified by TYPE,
 // placing the result in DATA.  If SWAP is TRUE, swap the bytes of
 // each element before copying to DATA.  FLT_FMT specifies the format
@@ -910,44 +962,12 @@
                         str =
                           octave::sys::env::make_absolute (p.find_first_of (names));
 
-                        size_t xpos
-                          = str.find_last_of (octave::sys::file_ops::dir_sep_chars ());
-
-                        std::string dir_name = str.substr (0, xpos);
-
-                        octave_value ov_fcn
-                          = octave::load_fcn_from_file (str, dir_name,
-                                                        "", "", fname);
-
-                        if (ov_fcn.is_defined ())
-                          tc = octave_value (new octave_fcn_handle (ov_fcn, fname));
-                        else
-                          {
-                            warning ("load: can't find the file %s",
-                                     fpath.c_str ());
-                            goto skip_ahead;
-                          }
+                        tc = manage_fcn_handle_creation (str, fname);
                       }
                   }
                 else
                   {
-                    size_t xpos
-                      = fpath.find_last_of (octave::sys::file_ops::dir_sep_chars ());
-
-                    std::string dir_name = fpath.substr (0, xpos);
-
-                    octave_value ov_fcn
-                      = octave::load_fcn_from_file (fpath, dir_name,
-                                                    "", "", fname);
-
-                    if (ov_fcn.is_defined ())
-                      tc = octave_value (new octave_fcn_handle (ov_fcn, fname));
-                    else
-                      {
-                        warning ("load: can't find the file %s",
-                                 fpath.c_str ());
-                        goto skip_ahead;
-                      }
+                    tc = manage_fcn_handle_creation (fpath, fname);
                   }
               }
           }
@@ -2679,3 +2699,5 @@
 
   return true;
 }
+
+
diff -r 9107bae20480 -r e6c50355c500 libinterp/octave-value/ov-base.h
--- a/libinterp/octave-value/ov-base.h	Wed Sep 06 09:25:46 2017 -0400
+++ b/libinterp/octave-value/ov-base.h	Wed Sep 06 17:16:44 2017 -0700
@@ -445,6 +445,8 @@
 
   virtual bool is_inline_function (void) const { return false; }
 
+  virtual bool is_dummy_function (void) const { return false; }
+
   virtual bool is_function (void) const { return false; }
 
   virtual bool is_user_script (void) const { return false; }
diff -r 9107bae20480 -r e6c50355c500 libinterp/octave-value/ov-fcn-handle.cc
--- a/libinterp/octave-value/ov-fcn-handle.cc	Wed Sep 06 09:25:46 2017 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.cc	Wed Sep 06 17:16:44 2017 -0700
@@ -96,6 +96,15 @@
     error ("handles to nested functions are not yet supported");
 }
 
+octave_fcn_handle::octave_fcn_handle (const std::string& n)
+  : nm (n), has_overloads (false), overloads ()
+{
+  octave_user_function *f = new octave_user_function ();
+  f->stash_function_name (n);
+  f->mark_as_dummy_function ();
+  fcn = octave_value (f);
+}
+
 octave_value_list
 octave_fcn_handle::subsref (const std::string& type,
                             const std::list<octave_value_list>& idx,
@@ -213,7 +222,12 @@
     {
       // Non-overloaded function (anonymous, subfunction, private function).
       if (fcn.is_defined ())
-        retval = octave::feval (fcn, args, nargout);
+        {
+          if (fcn.is_dummy_function ())
+            retval = octave::feval (fcn_name (), args, nargout);
+          else
+            retval = octave::feval (fcn, args, nargout);
+        }
       else
         error ("%s: no longer valid function handle", nm.c_str ());
     }
@@ -1642,29 +1656,35 @@
         }
 
       if (! any_match)
-        error ("@%s: no function and no method found", tnm.c_str ());
-
-      octave_fcn_handle *fh = new octave_fcn_handle (f, tnm);
-      retval = fh;
-
-      for (auto& cls : classes)
         {
-          std::string class_name = cls;
-          octave_value fmeth = symtab.find_method (tnm, class_name);
+          octave_fcn_handle *fh = new octave_fcn_handle (tnm);
+          retval = fh;
+        }
+      else
+        {
+          octave_fcn_handle *fh = new octave_fcn_handle (f, tnm);
+          retval = fh;
 
-          bool is_builtin = false;
-          for (int i = 0; i < btyp_num_types; i++)
+          for (auto& cls : classes)
             {
-              // FIXME: Too slow? Maybe binary lookup?
-              if (class_name == btyp_class_name[i])
+              std::string class_name = cls;
+              octave_value fmeth = symtab.find_method (tnm, class_name);
+
+              bool is_builtin = false;
+              for (int i = 0; i < btyp_num_types; i++)
                 {
-                  is_builtin = true;
-                  fh->set_overload (static_cast<builtin_type_t> (i), fmeth);
+                  // FIXME: Too slow? Maybe binary lookup?
+                  if (class_name == btyp_class_name[i])
+                    {
+                      is_builtin = true;
+                      fh->set_overload (static_cast<builtin_type_t> (i),
+                                        fmeth);
+                    }
                 }
-            }
 
-          if (! is_builtin)
-            fh->set_overload (class_name, fmeth);
+              if (! is_builtin)
+                fh->set_overload (class_name, fmeth);
+            }
         }
     }
 
diff -r 9107bae20480 -r e6c50355c500 libinterp/octave-value/ov-fcn-handle.h
--- a/libinterp/octave-value/ov-fcn-handle.h	Wed Sep 06 09:25:46 2017 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.h	Wed Sep 06 17:16:44 2017 -0700
@@ -40,6 +40,7 @@
   class tree_evaluator;
 }
 
+
 // Function handles.
 
 class
@@ -57,8 +58,7 @@
   octave_fcn_handle (void)
     : fcn (), nm (), has_overloads (false), overloads () { }
 
-  octave_fcn_handle (const std::string& n)
-    : fcn (), nm (n), has_overloads (false), overloads () { }
+  octave_fcn_handle (const std::string& n);
 
   octave_fcn_handle (const octave_value& f,  const std::string& n = anonymous);
 
diff -r 9107bae20480 -r e6c50355c500 libinterp/octave-value/ov-usr-fcn.cc
--- a/libinterp/octave-value/ov-usr-fcn.cc	Wed Sep 06 09:25:46 2017 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.cc	Wed Sep 06 17:16:44 2017 -0700
@@ -248,8 +248,8 @@
     num_named_args (param_list ? param_list->length () : 0),
     subfunction (false), inline_function (false),
     anonymous_function (false), nested_function (false),
-    class_constructor (none), class_method (false),
-    parent_scope (nullptr)
+    dummy_function (false), class_constructor (none),
+    class_method (false), parent_scope (nullptr)
 #if defined (HAVE_LLVM)
     , jit_info (0)
 #endif
diff -r 9107bae20480 -r e6c50355c500 libinterp/octave-value/ov-usr-fcn.h
--- a/libinterp/octave-value/ov-usr-fcn.h	Wed Sep 06 09:25:46 2017 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.h	Wed Sep 06 17:16:44 2017 -0700
@@ -320,6 +320,10 @@
 
   bool is_inline_function (void) const { return inline_function; }
 
+  void mark_as_dummy_function (void) { dummy_function = true; }
+
+  bool is_dummy_function (void) const { return dummy_function; }
+
   void mark_as_anonymous_function (void) { anonymous_function = true; }
 
   bool is_anonymous_function (void) const { return anonymous_function; }
@@ -476,6 +480,9 @@
   // TRUE means this is a nested function. (either a child or parent)
   bool nested_function;
 
+  // TRUE means this is a dummy function used by function handles
+  bool dummy_function;
+
   // Enum describing whether this function is the constructor for class object.
   class_ctor_type class_constructor;
 
diff -r 9107bae20480 -r e6c50355c500 libinterp/octave-value/ov.h
--- a/libinterp/octave-value/ov.h	Wed Sep 06 09:25:46 2017 -0400
+++ b/libinterp/octave-value/ov.h	Wed Sep 06 17:16:44 2017 -0700
@@ -750,6 +750,9 @@
   bool is_inline_function (void) const
   { return rep->is_inline_function (); }
 
+  bool is_dummy_function (void) const
+  { return rep->is_dummy_function (); }
+
   bool is_function (void) const
   { return rep->is_function (); }
 
diff -r 9107bae20480 -r e6c50355c500 test/module.mk
--- a/test/module.mk	Wed Sep 06 09:25:46 2017 -0400
+++ b/test/module.mk	Wed Sep 06 17:16:44 2017 -0700
@@ -69,6 +69,7 @@
 include %reldir%/fcn-handle-derived-resolution/module.mk
 include %reldir%/nest/module.mk
 include %reldir%/publish/module.mk
+include %reldir%/semi-lazy-fcn-handle/module.mk
 
 ALL_LOCAL_TARGETS += %reldir%/.gdbinit
 
diff -r 9107bae20480 -r e6c50355c500 test/semi-lazy-fcn-handle/lvl1/lvl2/slfh_test.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/semi-lazy-fcn-handle/lvl1/lvl2/slfh_test.m	Wed Sep 06 17:16:44 2017 -0700
@@ -0,0 +1,3 @@
+function ret = slfh_test
+  ret = 2;
+end
diff -r 9107bae20480 -r e6c50355c500 test/semi-lazy-fcn-handle/lvl1/slfh_test.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/semi-lazy-fcn-handle/lvl1/slfh_test.m	Wed Sep 06 17:16:44 2017 -0700
@@ -0,0 +1,3 @@
+function ret = slfh_test
+  ret = 1;
+end
diff -r 9107bae20480 -r e6c50355c500 test/semi-lazy-fcn-handle/module.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/semi-lazy-fcn-handle/module.mk	Wed Sep 06 17:16:44 2017 -0700
@@ -0,0 +1,6 @@
+semi_lazy_fcn_handle_TEST_FILES = \
+  %reldir%/lvl1/slfh_test.m \
+  %reldir%/lvl1/lvl2/slfh_test.m \
+  %reldir%/semi-lazy-fcn-handle.tst
+
+TEST_FILES += $(semi_lazy_fcn_handle_TEST_FILES)
diff -r 9107bae20480 -r e6c50355c500 test/semi-lazy-fcn-handle/semi-lazy-fcn-handle.tst
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/semi-lazy-fcn-handle/semi-lazy-fcn-handle.tst	Wed Sep 06 17:16:44 2017 -0700
@@ -0,0 +1,46 @@
+## Copyright (C) 2017 Piotr Held
+##
+## 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
+## <http://www.gnu.org/licenses/>.
+
+%% Test script for lazy function handle creation
+
+%% Make sure a handle created without any function in path behaves lazy
+%!test
+%! old_pwd = pwd;
+%! cleanup = @() cd (old_pwd);
+%! onCleanup (cleanup);
+%! fh = @slfh_test;
+%! fail ('fh ()', 'slfh_test');
+%! cd lvl1
+%! assert (fh (), 1);
+%! cd lvl2
+%! assert (fh (), 2);
+%! cd (old_pwd)
+
+%% Make sure that an eager function handle is always eager
+%!test
+%! old_pwd = pwd;
+%! cleanup = @() cd (old_pwd);
+%! onCleanup (cleanup);
+%! cd lvl1
+%! fh = @slfh_test;
+%! assert (fh (), 1);
+%! cd lvl2
+%! assert (fh (), 1);
+%! cd (old_pwd)
+%! assert (fh (), 1);
+
