changeset 29478:e88444be8468

maint: merge stable to default.
author John W. Eaton <jwe@octave.org>
date Thu, 01 Apr 2021 03:02:22 -0400
parents 34922160bda9 (current diff) 34d06c73b48d (diff)
children b99d87eafd4e
files libinterp/corefcn/call-stack.cc libinterp/corefcn/stack-frame.cc libinterp/corefcn/stack-frame.h libinterp/octave-value/cdef-object.cc libinterp/octave-value/cdef-object.h libinterp/octave-value/ov-base.h libinterp/octave-value/ov-cell.cc libinterp/octave-value/ov-cell.h libinterp/octave-value/ov-class.cc libinterp/octave-value/ov-class.h libinterp/octave-value/ov-classdef.h libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-struct.cc libinterp/octave-value/ov-struct.h libinterp/octave-value/ov.cc libinterp/octave-value/ov.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h test/module.mk
diffstat 25 files changed, 635 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/call-stack.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/corefcn/call-stack.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -412,7 +412,8 @@
   }
 
   void call_stack::push (octave_user_function *fcn,
-                         const stack_frame::local_vars_map& local_vars)
+                         const stack_frame::local_vars_map& local_vars,
+                         const std::shared_ptr<stack_frame>& closure_frames)
   {
     size_t new_frame_idx;
     std::shared_ptr<stack_frame> parent_link;
@@ -422,7 +423,8 @@
 
     std::shared_ptr<stack_frame>
       new_frame (stack_frame::create (m_evaluator, fcn, new_frame_idx,
-                                      parent_link, static_link, local_vars));
+                                      parent_link, static_link, local_vars,
+                                      closure_frames));
 
     m_cs.push_back (new_frame);
 
@@ -763,6 +765,9 @@
 
         m_curr_frame = caller->index ();
 
+        if (elt->is_closure_context ())
+          elt->break_closure_cycles (elt);
+
         m_cs.pop_back ();
       }
   }
--- a/libinterp/corefcn/call-stack.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/corefcn/call-stack.h	Thu Apr 01 03:02:22 2021 -0400
@@ -159,7 +159,8 @@
                const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
     void push (octave_user_function *fcn,
-               const stack_frame::local_vars_map& local_vars);
+               const stack_frame::local_vars_map& local_vars,
+               const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
     void push (octave_user_script *script);
 
--- a/libinterp/corefcn/stack-frame.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/corefcn/stack-frame.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -412,10 +412,13 @@
                           size_t index,
                           const std::shared_ptr<stack_frame>& parent_link,
                           const std::shared_ptr<stack_frame>& static_link,
-                          const local_vars_map& local_vars)
+                          const local_vars_map& local_vars,
+                          const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ())
       : base_value_stack_frame (tw, get_num_symbols (fcn), index,
                                 parent_link, static_link,
-                                get_access_link (fcn, static_link)),
+                                (access_link
+                                 ? access_link
+                                 : get_access_link (fcn, static_link))),
         m_fcn (fcn), m_unwind_protect_frame (nullptr)
     {
       // Initialize local variable values.
@@ -477,6 +480,8 @@
 
     void accept (stack_frame_walker& sfw);
 
+    void break_closure_cycles (const std::shared_ptr<stack_frame>& frame);
+
   private:
 
     // User-defined object associated with this stack frame.  Should
@@ -1061,9 +1066,10 @@
                                      octave_user_function *fcn, size_t index,
                                      const std::shared_ptr<stack_frame>& parent_link,
                                      const std::shared_ptr<stack_frame>& static_link,
-                                     const local_vars_map& local_vars)
+                                     const local_vars_map& local_vars,
+                                     const std::shared_ptr<stack_frame>& access_link)
   {
-    return new user_fcn_stack_frame (tw, fcn, index, parent_link, static_link, local_vars);
+    return new user_fcn_stack_frame (tw, fcn, index, parent_link, static_link, local_vars, access_link);
   }
 
   stack_frame * stack_frame::create (tree_evaluator& tw,
@@ -2352,6 +2358,15 @@
     sfw.visit_user_fcn_stack_frame (*this);
   }
 
+  void user_fcn_stack_frame::break_closure_cycles (const std::shared_ptr<stack_frame>& frame)
+  {
+    for (auto& val : m_values)
+      val.break_closure_cycles (frame);
+
+    if (m_access_link)
+      m_access_link->break_closure_cycles (frame);
+  }
+
   symbol_record scope_stack_frame::insert_symbol (const std::string& name)
   {
     // There is no access link for scope frames, so there is no other
--- a/libinterp/corefcn/stack-frame.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/corefcn/stack-frame.h	Thu Apr 01 03:02:22 2021 -0400
@@ -143,7 +143,8 @@
                  const std::shared_ptr<stack_frame>& parent_link,
                  const std::shared_ptr<stack_frame>& static_link,
                  const std::shared_ptr<stack_frame>& access_link)
-      : m_evaluator (tw), m_line (-1), m_column (-1), m_index (index),
+      : m_evaluator (tw), m_is_closure_context (false),
+        m_line (-1), m_column (-1), m_index (index),
         m_parent_link (parent_link), m_static_link (static_link),
         m_access_link (access_link), m_dispatch_class ()
     { }
@@ -172,7 +173,8 @@
     create (tree_evaluator& tw, octave_user_function *fcn, size_t index,
             const std::shared_ptr<stack_frame>& parent_link,
             const std::shared_ptr<stack_frame>& static_link,
-            const local_vars_map& local_vars);
+            const local_vars_map& local_vars,
+            const std::shared_ptr<stack_frame>& access_link = std::shared_ptr<stack_frame> ());
 
     // Scope.
     static stack_frame *
@@ -550,6 +552,11 @@
 
     virtual void accept (stack_frame_walker& sfw) = 0;
 
+    virtual void break_closure_cycles (const std::shared_ptr<stack_frame>&) { }
+
+    void mark_closure_context (void) { m_is_closure_context = true; }
+    bool is_closure_context (void) const { return m_is_closure_context; }
+
   protected:
 
     // Reference to the call stack that contains this frame.  Global
@@ -557,6 +564,10 @@
     // immediate access to them.
     tree_evaluator& m_evaluator;
 
+    // TRUE if this stack frame is saved with a handle to a nested
+    // function (closure).
+    bool m_is_closure_context;
+
     // The line and column of the source file where this stack frame
     // was created.  Used to print stack traces.
     int m_line;
--- a/libinterp/octave-value/cdef-object.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/cdef-object.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -499,6 +499,13 @@
       }
   }
 
+  void
+  cdef_object_scalar::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
+  {
+    for (octave_idx_type i = 0; i < map.nfields (); i++)
+      map.contents(i).break_closure_cycles (frame);
+  }
+
   octave_value_list
   cdef_object_scalar::subsref (const std::string& type,
                                const std::list<octave_value_list>& idx,
--- a/libinterp/octave-value/cdef-object.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/cdef-object.h	Thu Apr 01 03:02:22 2021 -0400
@@ -120,6 +120,11 @@
       err_invalid_object ("get_property");
     }
 
+    virtual void break_closure_cycles (const std::shared_ptr<octave::stack_frame>&)
+    {
+      err_invalid_object ("break_closure_cycles");
+    }
+
     virtual octave_value_list
     subsref (const std::string&, const std::list<octave_value_list>&,
              int, size_t&, const cdef_class&, bool)
@@ -276,6 +281,11 @@
       return rep->get_property (idx, pname);
     }
 
+    void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
+    {
+      rep->break_closure_cycles (frame);
+    }
+
     octave_value_list
     subsref (const std::string& type, const std::list<octave_value_list>& idx,
              int nargout, size_t& skip, const cdef_class& context,
@@ -456,6 +466,8 @@
 
     dim_vector dims (void) const { return dim_vector (1, 1); }
 
+    void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame);
+
     void put (const std::string& pname, const octave_value& val)
     {
       map.assign (pname, val);
--- a/libinterp/octave-value/ov-base.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-base.h	Thu Apr 01 03:02:22 2021 -0400
@@ -32,6 +32,7 @@
 
 #include <iosfwd>
 #include <list>
+#include <memory>
 #include <string>
 
 #include "Range.h"
@@ -44,6 +45,7 @@
 
 namespace octave
 {
+  class stack_frame;
   class type_info;
 
   // FIXME: This is not ideal, but it avoids including
@@ -272,6 +274,8 @@
   virtual octave_base_value *
   unique_clone (void) { return clone (); }
 
+  virtual void break_closure_cycles (const std::shared_ptr<octave::stack_frame>&) { }
+
   virtual type_conv_info
   numeric_conversion_function (void) const
   { return type_conv_info (); }
--- a/libinterp/octave-value/ov-cell.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-cell.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -50,6 +50,7 @@
 #include "utils.h"
 #include "ov-base-mat.h"
 #include "ov-base-mat.cc"
+#include "ov-fcn-handle.h"
 #include "ov-re-mat.h"
 #include "ov-scalar.h"
 #include "pr-output.h"
@@ -143,6 +144,12 @@
 
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_cell, "cell", "cell");
 
+void octave_cell::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
+{
+  for (octave_idx_type i = 0; i < matrix.numel (); i++)
+    matrix(i).break_closure_cycles (frame);
+}
+
 octave_value_list
 octave_cell::subsref (const std::string& type,
                       const std::list<octave_value_list>& idx,
--- a/libinterp/octave-value/ov-cell.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-cell.h	Thu Apr 01 03:02:22 2021 -0400
@@ -69,6 +69,8 @@
   octave_base_value * clone (void) const { return new octave_cell (*this); }
   octave_base_value * empty_clone (void) const { return new octave_cell (); }
 
+  void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame);
+
 #if 0
   octave_base_value * try_narrowing_conversion (void);
 #endif
--- a/libinterp/octave-value/ov-class.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-class.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -266,6 +266,18 @@
   error ("%s cannot be indexed with %c", nm.c_str (), t);
 }
 
+void
+octave_class::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
+{
+  for (octave_idx_type j = 0; j < m_map.nfields (); j++)
+    {
+      Cell& c = m_map.contents (j);
+
+      for (octave_idx_type i = 0; i < c.numel (); i++)
+        c(i).break_closure_cycles (frame);
+    }
+}
+
 Cell
 octave_class::dotref (const octave_value_list& idx)
 {
--- a/libinterp/octave-value/ov-class.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-class.h	Thu Apr 01 03:02:22 2021 -0400
@@ -88,6 +88,8 @@
     return new octave_class (octave_map (m_map.keys ()), c_name, m_parent_list);
   }
 
+  void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame);
+
   OCTINTERP_API Cell dotref (const octave_value_list& idx);
 
   OCTINTERP_API Matrix size (void);
--- a/libinterp/octave-value/ov-classdef.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-classdef.h	Thu Apr 01 03:02:22 2021 -0400
@@ -90,6 +90,11 @@
 
   OCTINTERP_API bool is_instance_of (const std::string& cls_name) const;
 
+  void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
+  {
+    m_object.break_closure_cycles (frame);
+  }
+
   OCTINTERP_API octave_value_list
   subsref (const std::string& type, const std::list<octave_value_list>& idx,
            int nargout);
--- a/libinterp/octave-value/ov-fcn-handle.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-fcn-handle.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -312,37 +312,29 @@
     std::list<std::string> m_parentage;
   };
 
-  class nested_fcn_handle : public base_fcn_handle
+  class base_nested_fcn_handle : public base_fcn_handle
   {
   public:
 
     // FIXME: octaveroot is temporary information used when loading
     // handles.  Can we avoid using it in the constructor?
 
-    nested_fcn_handle (const std::string& name = "",
-                       const std::string& file = "",
-                       const std::string& /*octaveroot*/ = "")
+    base_nested_fcn_handle (const std::string& name = "",
+                            const std::string& file = "",
+                            const std::string& /*octaveroot*/ = "")
       : base_fcn_handle (name, file)
     { }
 
-    nested_fcn_handle (const octave_value& fcn, const std::string& name,
-                       const std::shared_ptr<stack_frame>& closure_frames);
-
-    nested_fcn_handle (const nested_fcn_handle&) = default;
-
-    ~nested_fcn_handle (void) = default;
-
-    nested_fcn_handle * clone (void) const
-    {
-      return new nested_fcn_handle (*this);
-    }
+    base_nested_fcn_handle (const octave_value& fcn, const std::string& name)
+      : base_fcn_handle (name), m_fcn (fcn)
+    { }
 
     std::string type (void) const { return "nested"; }
 
+    using base_fcn_handle::is_nested;
+
     bool is_nested (void) const { return true; }
 
-    octave_value_list call (int nargout, const octave_value_list& args);
-
     // FIXME: These must go away.  They don't do the right thing for
     // scoping or overloads.
     octave_function * function_value (bool = false)
@@ -357,7 +349,7 @@
 
     octave_value fcn_val (void) { return m_fcn; }
 
-    octave_value workspace (void) const;
+    virtual octave_value workspace (void) const = 0;
 
     // Should be const.
     octave_scalar_map info (void);
@@ -378,16 +370,98 @@
     void print_raw (std::ostream&, bool pr_as_read_syntax,
                     int current_print_indent_level) const;
 
-    friend bool is_equal_to (const nested_fcn_handle& fh1,
-                             const nested_fcn_handle& fh2);
-
   protected:
 
     // The function we are handling.
     octave_value m_fcn;
+  };
+
+  class nested_fcn_handle : public base_nested_fcn_handle
+  {
+  public:
+
+    // FIXME: octaveroot is temporary information used when loading
+    // handles.  Can we avoid using it in the constructor?
+
+    nested_fcn_handle (const std::string& name = "",
+                       const std::string& file = "",
+                       const std::string& octaveroot = "")
+      : base_nested_fcn_handle (name, file, octaveroot)
+    { }
+
+    nested_fcn_handle (const octave_value& fcn, const std::string& name,
+                       const std::shared_ptr<stack_frame>& stack_context)
+      : base_nested_fcn_handle (fcn, name), m_stack_context (stack_context)
+    {
+      m_stack_context->mark_closure_context ();
+    }
+
+    nested_fcn_handle (const nested_fcn_handle&) = default;
+
+    ~nested_fcn_handle (void) = default;
+
+    using base_nested_fcn_handle::is_nested;
+
+    bool is_nested (const std::shared_ptr<stack_frame>& frame) const
+    {
+      return frame == m_stack_context;
+    }
+
+    nested_fcn_handle * clone (void) const
+    {
+      return new nested_fcn_handle (*this);
+    }
+
+    octave_value make_weak_nested_handle (void) const;
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    octave_value workspace (void) const;
+
+    friend bool is_equal_to (const nested_fcn_handle& fh1,
+                             const nested_fcn_handle& fh2);
+
+    std::shared_ptr<stack_frame> stack_context (void) const
+    {
+      return m_stack_context;
+    }
+
+  protected:
 
     // Pointer to closure stack frames.
-    std::shared_ptr<stack_frame> m_closure_frames;
+    std::shared_ptr<stack_frame> m_stack_context;
+  };
+
+  class weak_nested_fcn_handle : public base_nested_fcn_handle
+  {
+  public:
+
+    weak_nested_fcn_handle (const nested_fcn_handle& nfh)
+      : base_nested_fcn_handle (nfh), m_stack_context (nfh.stack_context ())
+    { }
+
+    weak_nested_fcn_handle (const weak_nested_fcn_handle&) = default;
+
+    ~weak_nested_fcn_handle (void) = default;
+
+    weak_nested_fcn_handle * clone (void) const
+    {
+      return new weak_nested_fcn_handle (*this);
+    }
+
+    bool is_weak_nested (void) const { return true; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    octave_value workspace (void) const;
+
+    friend bool is_equal_to (const weak_nested_fcn_handle& fh1,
+                             const weak_nested_fcn_handle& fh2);
+
+  protected:
+
+    // Pointer to closure stack frames.
+    std::weak_ptr<stack_frame> m_stack_context;
   };
 
   class class_simple_fcn_handle : public base_fcn_handle
@@ -501,7 +575,7 @@
     std::string m_dispatch_class;
   };
 
-  class anonymous_fcn_handle : public base_fcn_handle
+  class base_anonymous_fcn_handle : public base_fcn_handle
   {
   public:
 
@@ -512,28 +586,23 @@
     // tag in the binary file format.  See also the save_binary and
     // load_binary functions.
 
-    anonymous_fcn_handle (const std::string& name = "")
+    base_anonymous_fcn_handle (const std::string& name = "")
       : base_fcn_handle (name)
     { }
 
-    anonymous_fcn_handle (const octave_value& fcn,
-                          const stack_frame::local_vars_map& local_vars);
-
-    anonymous_fcn_handle (const anonymous_fcn_handle&) = default;
-
-    ~anonymous_fcn_handle (void) = default;
-
-    anonymous_fcn_handle * clone (void) const
-    {
-      return new anonymous_fcn_handle (*this);
-    }
+    base_anonymous_fcn_handle (const octave_value& fcn,
+                               const stack_frame::local_vars_map& local_vars)
+      : base_fcn_handle (anonymous), m_fcn (fcn), m_local_vars (local_vars)
+    { }
+
+    base_anonymous_fcn_handle (const base_anonymous_fcn_handle&) = default;
+
+    ~base_anonymous_fcn_handle (void) = default;
 
     std::string type (void) const { return "anonymous"; }
 
     bool is_anonymous (void) const { return true; }
 
-    octave_value_list call (int nargout, const octave_value_list& args);
-
     // FIXME: These must go away.  They don't do the right thing for
     // scoping or overloads.
     octave_function * function_value (bool = false)
@@ -548,7 +617,7 @@
 
     octave_value fcn_val (void) { return m_fcn; }
 
-    octave_value workspace (void) const;
+    virtual octave_value workspace (void) const = 0;
 
     // Should be const.
     octave_scalar_map info (void);
@@ -574,9 +643,6 @@
 
     bool parse (const std::string& fcn_text);
 
-    friend bool is_equal_to (const anonymous_fcn_handle& fh1,
-                             const anonymous_fcn_handle& fh2);
-
   protected:
 
     // The function we are handling.
@@ -586,7 +652,87 @@
     stack_frame::local_vars_map m_local_vars;
   };
 
-  const std::string anonymous_fcn_handle::anonymous ("@<anonymous>");
+  class anonymous_fcn_handle : public base_anonymous_fcn_handle
+  {
+  public:
+
+    using base_anonymous_fcn_handle::anonymous;
+
+    // Setting NAME here is a bit of a kluge to cope with a bad choice
+    // made to append the number of local variables to the @<anonymous>
+    // tag in the binary file format.  See also the save_binary and
+    // load_binary functions.
+
+    anonymous_fcn_handle (const std::string& name = "")
+      : base_anonymous_fcn_handle (name), m_stack_context ()
+    { }
+
+    anonymous_fcn_handle (const octave_value& fcn,
+                          const stack_frame::local_vars_map& local_vars,
+                          const std::shared_ptr<stack_frame>& stack_context = std::shared_ptr<stack_frame> ());
+
+    anonymous_fcn_handle (const anonymous_fcn_handle&) = default;
+
+    ~anonymous_fcn_handle (void) = default;
+
+    anonymous_fcn_handle * clone (void) const
+    {
+      return new anonymous_fcn_handle (*this);
+    }
+
+    octave_value make_weak_anonymous_handle (void) const;
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    octave_value workspace (void) const;
+
+    friend bool is_equal_to (const anonymous_fcn_handle& fh1,
+                             const anonymous_fcn_handle& fh2);
+
+    std::shared_ptr<stack_frame> stack_context (void) const
+    {
+      return m_stack_context;
+    }
+
+  protected:
+
+    // Pointer to closure stack frames.
+    std::shared_ptr<stack_frame> m_stack_context;
+  };
+
+  class weak_anonymous_fcn_handle : public base_anonymous_fcn_handle
+  {
+  public:
+
+    using base_anonymous_fcn_handle::anonymous;
+
+    weak_anonymous_fcn_handle (const anonymous_fcn_handle& afh)
+      : base_anonymous_fcn_handle (afh), m_stack_context (afh.stack_context ())
+    { }
+
+    weak_anonymous_fcn_handle (const weak_anonymous_fcn_handle&) = default;
+
+    ~weak_anonymous_fcn_handle (void) = default;
+
+    weak_anonymous_fcn_handle * clone (void) const
+    {
+      return new weak_anonymous_fcn_handle (*this);
+    }
+
+    bool is_weak_anonymous (void) const { return true; }
+
+    octave_value_list call (int nargout, const octave_value_list& args);
+
+    octave_value workspace (void) const;
+
+    friend bool is_equal_to (const weak_anonymous_fcn_handle& fh1,
+                             const weak_anonymous_fcn_handle& fh2);
+
+  protected:
+
+    // Pointer to closure stack frames.
+    std::weak_ptr<stack_frame> m_stack_context;
+  };
 
   extern bool is_equal_to (const anonymous_fcn_handle& fh1,
                            const anonymous_fcn_handle& fh2);
@@ -597,6 +743,20 @@
            name.c_str ());
   }
 
+  octave_value base_fcn_handle::make_weak_nested_handle (void) const
+  {
+    std::string type_str = type ();
+    error ("invalid conversion from %s handle to weak nestead handle",
+           type_str.c_str ());
+  }
+
+  octave_value base_fcn_handle::make_weak_anonymous_handle (void) const
+  {
+    std::string type_str = type ();
+    error ("invalid conversion from %s handle to weak anonymous handle",
+           type_str.c_str ());
+  }
+
   octave_value_list
   base_fcn_handle::subsref (const std::string& type,
                             const std::list<octave_value_list>& idx,
@@ -1500,32 +1660,7 @@
       }
   }
 
-  nested_fcn_handle::nested_fcn_handle (const octave_value& fcn,
-                                        const std::string& name,
-                                        const std::shared_ptr<stack_frame>& closure_frames)
-    : base_fcn_handle (name), m_fcn (fcn), m_closure_frames (closure_frames)
-  { }
-
-  octave_value_list
-  nested_fcn_handle::call (int nargout, const octave_value_list& args)
-  {
-    tree_evaluator& tw = __get_evaluator__ ("nested_fcn_handle::call");
-
-    octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
-
-    tw.push_stack_frame (oct_usr_fcn, m_closure_frames);
-
-    unwind_action act ([&tw] () { tw.pop_stack_frame (); });
-
-    return oct_usr_fcn->execute (tw, nargout, args);
-  }
-
-  octave_value nested_fcn_handle::workspace (void) const
-  {
-    return m_closure_frames->workspace ();
-  }
-
-  octave_scalar_map nested_fcn_handle::info (void)
+  octave_scalar_map base_nested_fcn_handle::info (void)
   {
     octave_scalar_map m;
 
@@ -1546,7 +1681,7 @@
   // Is it an error if that fails?  Or should this job always be
   // deferred until the handle is used?
 
-  bool nested_fcn_handle::save_ascii (std::ostream& os)
+  bool base_nested_fcn_handle::save_ascii (std::ostream& os)
   {
     unimplemented ("save", "text");
 
@@ -1555,7 +1690,7 @@
     return true;
   }
 
-  bool nested_fcn_handle::load_ascii (std::istream& is)
+  bool base_nested_fcn_handle::load_ascii (std::istream& is)
   {
     unimplemented ("load", "text");
 
@@ -1564,7 +1699,8 @@
     return true;
   }
 
-  bool nested_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
+  bool base_nested_fcn_handle::save_binary (std::ostream& os,
+                                            bool save_as_floats)
   {
     unimplemented ("save", "binary");
 
@@ -1574,8 +1710,8 @@
     return true;
   }
 
-  bool nested_fcn_handle::load_binary (std::istream& is, bool swap,
-                                       mach_info::float_format fmt)
+  bool base_nested_fcn_handle::load_binary (std::istream& is, bool swap,
+                                            mach_info::float_format fmt)
   {
     unimplemented ("load", "binary");
 
@@ -1586,8 +1722,8 @@
     return true;
   }
 
-  bool nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
-                                     bool)
+  bool base_nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
+                                          const char *name, bool)
   {
 #if defined (HAVE_HDF5)
 
@@ -1610,9 +1746,9 @@
 #endif
   }
 
-  bool nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
-                                     octave_hdf5_id& space_hid,
-                                     octave_hdf5_id& type_hid)
+  bool base_nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
+                                          octave_hdf5_id& space_hid,
+                                          octave_hdf5_id& type_hid)
   {
 #if defined (HAVE_HDF5)
 
@@ -1635,14 +1771,39 @@
 #endif
   }
 
-  void nested_fcn_handle::print_raw (std::ostream& os,
-                                     bool pr_as_read_syntax,
-                                     int current_print_indent_level) const
+  void base_nested_fcn_handle::print_raw (std::ostream& os,
+                                          bool pr_as_read_syntax,
+                                          int current_print_indent_level) const
   {
     octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
                            current_print_indent_level);
   }
 
+  octave_value nested_fcn_handle::make_weak_nested_handle (void) const
+  {
+    return octave_value (new octave_fcn_handle
+                         (new weak_nested_fcn_handle (*this)));
+  }
+
+  octave_value_list
+  nested_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    tree_evaluator& tw = __get_evaluator__ ("nested_fcn_handle::call");
+
+    octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
+
+    tw.push_stack_frame (oct_usr_fcn, m_stack_context);
+
+    unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+    return oct_usr_fcn->execute (tw, nargout, args);
+  }
+
+  octave_value nested_fcn_handle::workspace (void) const
+  {
+    return m_stack_context->workspace ();
+  }
+
   bool is_equal_to (const nested_fcn_handle& fh1, const nested_fcn_handle& fh2)
   {
     if (fh1.m_name == fh2.m_name
@@ -1652,6 +1813,39 @@
       return false;
   }
 
+  octave_value_list
+  weak_nested_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    tree_evaluator& tw = __get_evaluator__ ("weak_nested_fcn_handle::call");
+
+    octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
+
+    std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
+
+    tw.push_stack_frame (oct_usr_fcn, frames);
+
+    unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+    return oct_usr_fcn->execute (tw, nargout, args);
+  }
+
+  octave_value weak_nested_fcn_handle::workspace (void) const
+  {
+    std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
+
+    return frames ? frames->workspace () : octave_value ();
+  }
+
+  bool is_equal_to (const weak_nested_fcn_handle& fh1,
+                    const weak_nested_fcn_handle& fh2)
+  {
+    if (fh1.m_name == fh2.m_name
+        && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
+      return fh1.m_fcn.is_copy_of (fh2.m_fcn);
+    else
+      return false;
+  }
+
   class_simple_fcn_handle::class_simple_fcn_handle (const std::string& class_nm,
                                                     const std::string& meth_nm)
     : base_fcn_handle (meth_nm), m_obj (), m_fcn (),
@@ -1830,36 +2024,9 @@
       return false;
   }
 
-  anonymous_fcn_handle::anonymous_fcn_handle (const octave_value& fcn,
-                                              const stack_frame::local_vars_map& local_vars)
-    : base_fcn_handle (anonymous), m_fcn (fcn), m_local_vars (local_vars)
-  { }
-
-  octave_value_list
-  anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
-  {
-    tree_evaluator& tw = __get_evaluator__ ("anonymous_fcn_handle::call");
-
-    octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
-
-    tw.push_stack_frame (oct_usr_fcn, m_local_vars);
-
-    unwind_action act ([&tw] () { tw.pop_stack_frame (); });
-
-    return oct_usr_fcn->execute (tw, nargout, args);
-  }
-
-  octave_value anonymous_fcn_handle::workspace (void) const
-  {
-    octave_scalar_map ws;
-
-    for (const auto& nm_val : m_local_vars)
-      ws.assign (nm_val.first, nm_val.second);
-
-    return ws;
-  }
-
-  octave_scalar_map anonymous_fcn_handle::info (void)
+  const std::string base_anonymous_fcn_handle::anonymous ("@<anonymous>");
+
+  octave_scalar_map base_anonymous_fcn_handle::info (void)
   {
     octave_scalar_map m;
 
@@ -1869,13 +2036,13 @@
 
     m.setfield ("type", type ());
     m.setfield ("file", "");
-    m.setfield ("workspace", Cell (workspace ()));
+    m.setfield ("workspace", workspace ());
     m.setfield ("within_file_path", "");
 
     return m;
   }
 
-  bool anonymous_fcn_handle::save_ascii (std::ostream& os)
+  bool base_anonymous_fcn_handle::save_ascii (std::ostream& os)
   {
     // FIXME: can we ensure that m_fcn is always defined?
 
@@ -1903,7 +2070,7 @@
     return true;
   }
 
-  bool anonymous_fcn_handle::load_ascii (std::istream& is)
+  bool base_anonymous_fcn_handle::load_ascii (std::istream& is)
   {
     skip_preceeding_newline (is);
 
@@ -1923,7 +2090,7 @@
     // defines the anonymous function.
 
     interpreter& interp
-      = __get_interpreter__ ("anonymous_fcn_handle::load_ascii");
+      = __get_interpreter__ ("base_anonymous_fcn_handle::load_ascii");
 
     tree_evaluator& tw = interp.get_evaluator ();
 
@@ -1962,7 +2129,8 @@
     return false;
   }
 
-  bool anonymous_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
+  bool base_anonymous_fcn_handle::save_binary (std::ostream& os,
+                                               bool save_as_floats)
   {
     // FIXME: can we ensure that m_fcn is always defined?
 
@@ -2002,8 +2170,8 @@
     return true;
   }
 
-  bool anonymous_fcn_handle::load_binary (std::istream& is, bool swap,
-                                          mach_info::float_format fmt)
+  bool base_anonymous_fcn_handle::load_binary (std::istream& is, bool swap,
+                                               mach_info::float_format fmt)
   {
     // Read extra characters in m_name as the number of local variable
     // values in this anonymous function.
@@ -2039,7 +2207,7 @@
     // defines the anonymous function.
 
     interpreter& interp
-      = __get_interpreter__ ("anonymous_fcn_handle::load_binary");
+      = __get_interpreter__ ("base_anonymous_fcn_handle::load_binary");
 
     tree_evaluator& tw = interp.get_evaluator ();
 
@@ -2070,8 +2238,9 @@
     return false;
   }
 
-  bool anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
-                                        const char *name, bool save_as_floats)
+  bool base_anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
+                                             const char *name,
+                                             bool save_as_floats)
   {
 #if defined (HAVE_HDF5)
 
@@ -2235,9 +2404,9 @@
 #endif
   }
 
-  bool anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
-                                        octave_hdf5_id& space_hid,
-                                        octave_hdf5_id& type_hid)
+  bool base_anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
+                                             octave_hdf5_id& space_hid,
+                                             octave_hdf5_id& type_hid)
   {
 #if defined (HAVE_HDF5)
 
@@ -2353,7 +2522,7 @@
     // defines the anonymous function.
 
     interpreter& interp
-      = __get_interpreter__ ("anonymous_fcn_handle::load_hdf5");
+      = __get_interpreter__ ("base_anonymous_fcn_handle::load_hdf5");
 
     tree_evaluator& tw = interp.get_evaluator ();
 
@@ -2402,7 +2571,7 @@
 #endif
   }
 
-  void anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const
+  void base_anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const
   {
     tree_print_code tpc (os);
 
@@ -2443,8 +2612,7 @@
     tpc.print_fcn_handle_body (e);
   }
 
-  bool
-  anonymous_fcn_handle::parse (const std::string& fcn_text)
+  bool base_anonymous_fcn_handle::parse (const std::string& fcn_text)
   {
     // FIXME: If evaluation of the string gives us an anonymous function
     // handle object, then why extract the function and create a new
@@ -2452,7 +2620,8 @@
     // values to the object returned by eval_string?  This code is also is
     // duplicated in read_mat5_binary_element in ls-mat5.cc.
 
-    interpreter& interp = __get_interpreter__ ("anonymous_fcn_handle::parse");
+    interpreter& interp
+      = __get_interpreter__ ("base_anonymous_fcn_handle::parse");
 
     // Set up temporary scope to use for evaluating the text that defines
     // the anonymous function so that we don't pick up values of random
@@ -2490,6 +2659,62 @@
     return true;
   }
 
+  anonymous_fcn_handle::anonymous_fcn_handle (const octave_value& fcn,
+                                              const stack_frame::local_vars_map& local_vars,
+                                              const std::shared_ptr<stack_frame>& stack_context)
+    : base_anonymous_fcn_handle (fcn, local_vars),
+      m_stack_context (stack_context)
+  { }
+
+  octave_value anonymous_fcn_handle::make_weak_anonymous_handle (void) const
+  {
+    return octave_value (new octave_fcn_handle
+                         (new weak_anonymous_fcn_handle (*this)));
+  }
+
+  octave_value_list
+  anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    tree_evaluator& tw = __get_evaluator__ ("anonymous_fcn_handle::call");
+
+    octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
+
+    tw.push_stack_frame (oct_usr_fcn, m_local_vars, m_stack_context);
+
+    unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+    return oct_usr_fcn->execute (tw, nargout, args);
+  }
+
+  octave_value anonymous_fcn_handle::workspace (void) const
+  {
+    octave_scalar_map local_vars_map;
+
+    for (const auto& nm_val : m_local_vars)
+      local_vars_map.assign (nm_val.first, nm_val.second);
+
+    // FIXME: it would be more convenient if stack_frame::workspace
+    // returned a Cell object directly instead of a Cell in an
+    // octave_value object.
+
+    Cell cell_frames;
+
+    if (m_stack_context)
+      {
+        octave_value ov_frames = m_stack_context->workspace ();
+        cell_frames = ov_frames.cell_value ();
+      }
+
+    octave_idx_type num_frames = cell_frames.numel ();
+    // FIXME: It seems there should be a simple way to concatenate cells...
+    Cell retval = Cell (num_frames+1, 1);
+    retval(0) = m_local_vars;
+    for (octave_idx_type i = 0; i < num_frames; i++)
+      retval(i+1) = cell_frames(i);
+
+    return retval;
+  }
+
   bool is_equal_to (const anonymous_fcn_handle& fh1,
                     const anonymous_fcn_handle& fh2)
   {
@@ -2498,6 +2723,65 @@
     else
       return false;
   }
+
+  octave_value_list
+  weak_anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
+  {
+    tree_evaluator& tw = __get_evaluator__ ("anonymous_fcn_handle::call");
+
+    octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
+
+    std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
+
+    tw.push_stack_frame (oct_usr_fcn, m_local_vars, frames);
+
+    unwind_action act ([&tw] () { tw.pop_stack_frame (); });
+
+    return oct_usr_fcn->execute (tw, nargout, args);
+  }
+
+  octave_value weak_anonymous_fcn_handle::workspace (void) const
+  {
+    octave_scalar_map local_vars_map;
+
+    for (const auto& nm_val : m_local_vars)
+      local_vars_map.assign (nm_val.first, nm_val.second);
+
+    // FIXME: it would be more convenient if stack_frame::workspace
+    // returned a Cell object directly instead of a Cell in an
+    // octave_value object.
+
+    std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
+
+    Cell cell_frames;
+
+    if (frames)
+      {
+        octave_value ov_frames = frames->workspace ();
+        cell_frames = ov_frames.cell_value ();
+      }
+
+    octave_idx_type num_frames = cell_frames.numel ();
+
+    // FIXME: It seems there should be a simple way to concatenate
+    // cells...
+    Cell retval = Cell (num_frames+1, 1);
+    retval(0) = m_local_vars;
+    for (octave_idx_type i = 0; i < num_frames; i++)
+      retval(i+1) = cell_frames(i);
+
+    return retval;
+  }
+
+  bool is_equal_to (const weak_anonymous_fcn_handle& fh1,
+                    const weak_anonymous_fcn_handle& fh2)
+  {
+    if (fh1.m_name == fh2.m_name
+        && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
+      return fh1.m_fcn.is_copy_of (fh2.m_fcn);
+    else
+      return false;
+  }
 }
 
 octave_fcn_handle::octave_fcn_handle (void)
@@ -2547,15 +2831,16 @@
 
 octave_fcn_handle::octave_fcn_handle (const octave_value& fcn,
                                       const std::string& name,
-                                      const std::shared_ptr<octave::stack_frame>& closure_frames)
+                                      const std::shared_ptr<octave::stack_frame>& stack_context)
   : octave_base_value (),
-    m_rep (new octave::nested_fcn_handle (fcn, name, closure_frames))
+    m_rep (new octave::nested_fcn_handle (fcn, name, stack_context))
 { }
 
 octave_fcn_handle::octave_fcn_handle (const octave_value& fcn,
-                                      const octave::stack_frame::local_vars_map& local_vars)
+                                      const octave::stack_frame::local_vars_map& local_vars,
+                                      const std::shared_ptr<octave::stack_frame>& stack_context)
   : octave_base_value (),
-    m_rep (new octave::anonymous_fcn_handle (fcn, local_vars))
+    m_rep (new octave::anonymous_fcn_handle (fcn, local_vars, stack_context))
 { }
 
 octave_fcn_handle::octave_fcn_handle (octave::base_fcn_handle *rep)
--- a/libinterp/octave-value/ov-fcn-handle.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-fcn-handle.h	Thu Apr 01 03:02:22 2021 -0400
@@ -72,10 +72,23 @@
 
     virtual bool is_nested (void) const { return false; }
 
+    virtual bool is_nested (const std::shared_ptr<stack_frame>&) const
+    {
+      return false;
+    }
+
+    virtual bool is_weak_nested (void) const { return false; }
+
     virtual bool is_class_simple (void) const { return false; }
 
     virtual bool is_anonymous (void) const { return false; }
 
+    virtual bool is_weak_anonymous (void) const { return false; }
+
+    virtual octave_value make_weak_nested_handle (void) const;
+
+    virtual octave_value make_weak_anonymous_handle (void) const;
+
     std::string fcn_name (void) const { return m_name; }
 
     std::string file (void) const { return m_file; }
@@ -203,7 +216,11 @@
   // Create an anonymous function handle with local variable values
   // provided in LOCAL_VARS.
   octave_fcn_handle (const octave_value& fcn,
-                     const octave::stack_frame::local_vars_map& local_vars);
+                     const octave::stack_frame::local_vars_map& local_vars,
+                     const std::shared_ptr<octave::stack_frame>& closure_frames
+                       = std::shared_ptr<octave::stack_frame> ());
+
+  octave_fcn_handle (octave::base_fcn_handle *rep);
 
   octave_fcn_handle (const octave_fcn_handle& fh);
 
@@ -254,10 +271,29 @@
 
   bool is_nested (void) const { return m_rep->is_nested (); }
 
+  bool is_nested (const std::shared_ptr<octave::stack_frame>& frame) const
+  {
+    return m_rep->is_nested (frame);
+  }
+
+  bool is_weak_nested (void) const { return m_rep->is_weak_nested (); }
+
   bool is_class_simple (void) const { return m_rep->is_class_simple (); }
 
   bool is_anonymous (void) const { return m_rep->is_anonymous (); }
 
+  bool is_weak_anonymous (void) const { return m_rep->is_weak_anonymous (); }
+
+  octave_value make_weak_nested_handle (void) const
+  {
+    return m_rep->make_weak_nested_handle ();
+  }
+
+  octave_value make_weak_anonymous_handle (void) const
+  {
+    return m_rep->make_weak_anonymous_handle ();
+  }
+
   dim_vector dims (void) const;
 
   // FIXME: These must go away.  They don't do the right thing for
@@ -328,8 +364,6 @@
 
   std::shared_ptr<octave::base_fcn_handle> m_rep;
 
-  octave_fcn_handle (octave::base_fcn_handle *rep);
-
   octave::base_fcn_handle * get_rep (void) const { return m_rep.get (); }
 
   DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
--- a/libinterp/octave-value/ov-struct.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-struct.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -63,6 +63,18 @@
 // specified by struct_levels_to_print.
 static bool Vprint_struct_array_contents = false;
 
+void
+octave_struct::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
+{
+  for (octave_idx_type j = 0; j < map.nfields (); j++)
+    {
+      Cell& c = map.contents (j);
+
+      for (octave_idx_type i = 0; i < c.numel (); i++)
+        c(i).break_closure_cycles (frame);
+    }
+}
+
 octave_base_value *
 octave_struct::try_narrowing_conversion (void)
 {
@@ -1083,6 +1095,13 @@
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(octave_scalar_struct, "scalar struct",
                                     "struct");
 
+void
+octave_scalar_struct::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
+{
+  for (octave_idx_type i = 0; i < map.nfields (); i++)
+    map.contents(i).break_closure_cycles (frame);
+}
+
 octave_value
 octave_scalar_struct::dotref (const octave_value_list& idx, bool auto_add)
 {
--- a/libinterp/octave-value/ov-struct.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov-struct.h	Thu Apr 01 03:02:22 2021 -0400
@@ -64,6 +64,8 @@
   octave_base_value * clone (void) const { return new octave_struct (*this); }
   octave_base_value * empty_clone (void) const { return new octave_struct (); }
 
+  void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame);
+
   octave_base_value * try_narrowing_conversion (void);
 
   Cell dotref (const octave_value_list& idx, bool auto_add = false);
@@ -192,6 +194,8 @@
   octave_base_value * empty_clone (void) const
   { return new octave_scalar_struct (); }
 
+  void break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame);
+
   octave_value dotref (const octave_value_list& idx, bool auto_add = false);
 
   octave_value subsref (const std::string& type,
--- a/libinterp/octave-value/ov.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -1241,6 +1241,33 @@
 }
 
 void
+octave_value::break_closure_cycles (const std::shared_ptr<octave::stack_frame>& frame)
+{
+  if (is_function_handle ())
+    {
+      octave_fcn_handle *fhdl = rep->fcn_handle_value ();
+
+      if (fhdl->is_nested (frame) && ! fhdl->is_weak_nested ())
+        *this = fhdl->make_weak_nested_handle ();
+      else if (fhdl->is_anonymous () && ! fhdl->is_weak_anonymous ())
+        *this = fhdl->make_weak_anonymous_handle ();
+    }
+  else
+    {
+      // FIXME: Is there a efficient way to avoid calling make_unique
+      // if REP doesn't contain any nested function handles?
+      //
+      // Probably we should be asking REP to make a modified copy IFF it
+      // is needed, then replace our REP with that if a copy is made,
+      // otherwise we leave it alone.
+
+      make_unique ();
+
+      rep->break_closure_cycles (frame);
+    }
+}
+
+void
 octave_value::maybe_mutate (void)
 {
   octave_base_value *tmp = rep->try_narrowing_conversion ();
--- a/libinterp/octave-value/ov.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/octave-value/ov.h	Thu Apr 01 03:02:22 2021 -0400
@@ -33,6 +33,7 @@
 #include <iosfwd>
 #include <string>
 #include <list>
+#include <memory>
 #include <map>
 
 #include "Range.h"
@@ -46,6 +47,7 @@
 
 namespace octave
 {
+  class stack_frame;
   class type_info;
 }
 
@@ -388,6 +390,12 @@
       }
   }
 
+  // Convert any nested function handles in this object to use weak
+  // references to their enclosing stack frame context.  Used to break
+  // shared_ptr reference cycles for handles to nested functions
+  // (closures).
+  void break_closure_cycles (const std::shared_ptr<octave::stack_frame>&);
+
   // Simple assignment.
 
   octave_value& operator = (const octave_value& a)
--- a/libinterp/parse-tree/pt-eval.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/parse-tree/pt-eval.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -2291,9 +2291,10 @@
   }
 
   void tree_evaluator::push_stack_frame (octave_user_function *fcn,
-                                         const stack_frame::local_vars_map& local_vars)
-  {
-    m_call_stack.push (fcn, local_vars);
+                                         const stack_frame::local_vars_map& local_vars,
+                                         const std::shared_ptr<stack_frame>& closure_frames)
+  {
+    m_call_stack.push (fcn, local_vars, closure_frames);
   }
 
   void tree_evaluator::push_stack_frame (octave_user_script *script)
@@ -3319,11 +3320,6 @@
 
     unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_FUNCTION);
 
-    unwind_action act1 ([] (std::shared_ptr<stack_frame> frame)
-                       {
-                         frame->clear_values ();
-                       }, m_call_stack.get_current_stack_frame ());
-
     tree_statement_list *cmd_list = user_function.body ();
 
     if (cmd_list)
--- a/libinterp/parse-tree/pt-eval.h	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/parse-tree/pt-eval.h	Thu Apr 01 03:02:22 2021 -0400
@@ -411,7 +411,8 @@
                            const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
     void push_stack_frame (octave_user_function *fcn,
-                           const stack_frame::local_vars_map& local_vars);
+                           const stack_frame::local_vars_map& local_vars,
+                           const std::shared_ptr<stack_frame>& closure_frames = std::shared_ptr<stack_frame> ());
 
     void push_stack_frame (octave_user_script *script);
 
--- a/libinterp/parse-tree/pt-fcn-handle.cc	Wed Mar 31 21:33:46 2021 +0200
+++ b/libinterp/parse-tree/pt-fcn-handle.cc	Thu Apr 01 03:02:22 2021 -0400
@@ -151,6 +151,8 @@
 
     octave_function *curr_fcn = cs.current_function ();
 
+    bool is_nested = false;
+
     if (curr_fcn)
       {
         // FIXME: maybe it would be better to just stash curr_fcn
@@ -162,6 +164,7 @@
 
         if (curr_fcn->is_parent_function () || curr_fcn->is_nested_function ())
           {
+            is_nested = true;
             af->mark_as_nested_function ();
             new_scope.set_nesting_depth (parent_scope.nesting_depth () + 1);
           }
@@ -185,7 +188,9 @@
 
     octave_value ov_fcn (af);
 
-    return octave_value (new octave_fcn_handle (ov_fcn, local_vars));
+    return (is_nested
+            ? octave_value (new octave_fcn_handle (ov_fcn, local_vars, frame))
+            : octave_value (new octave_fcn_handle (ov_fcn, local_vars)));
   }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug-60237/bug-60237.tst	Thu Apr 01 03:02:22 2021 -0400
@@ -0,0 +1,1 @@
+%!assert (bug_60237 (), 5)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug-60237/bug_60237.m	Thu Apr 01 03:02:22 2021 -0400
@@ -0,0 +1,11 @@
+function r = bug_60237 ()
+  d = 2;
+  function c = bm (a)
+    c = a + d;
+  endfunction;
+  r = ancall (@(a) bm (a), 2);
+endfunction
+
+function r = ancall (f, a)
+  r = f (a) + 1;
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug-60237/module.mk	Thu Apr 01 03:02:22 2021 -0400
@@ -0,0 +1,5 @@
+bug_60237_TEST_FILES = \
+  %reldir%/bug-60237.tst \
+  %reldir%/bug_60237.m
+
+TEST_FILES += $(bug_60237_TEST_FILES)
--- a/test/module.mk	Wed Mar 31 21:33:46 2021 +0200
+++ b/test/module.mk	Thu Apr 01 03:02:22 2021 -0400
@@ -93,6 +93,7 @@
 include %reldir%/bug-59661/module.mk
 include %reldir%/bug-59704/module.mk
 include %reldir%/bug-59937/module.mk
+include %reldir%/bug-60237/module.mk
 include %reldir%/class-concat/module.mk
 include %reldir%/classdef/module.mk
 include %reldir%/classdef-multiple-inheritance/module.mk