changeset 32319:7903910181a9

VM Support nested anonymous functions that access outer locals (Bug #64688) Add dummy locals to all outer nesting anonymous function frames, to be able to set them in the inner one. * libinterp/parse-tree/pt-bytecode-walk.cc: Add dummy locals * test/compile/bytecode_anon_handles.m: Update test
author Petter T. <petter.vilhelm@gmail.com>
date Wed, 20 Sep 2023 20:02:12 -0400
parents ddf7c5f4ddde
children 04599c7712ec
files libinterp/parse-tree/pt-bytecode-walk.cc test/compile/bytecode_anon_handles.m
diffstat 2 files changed, 34 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/parse-tree/pt-bytecode-walk.cc	Wed Sep 20 20:01:27 2023 -0400
+++ b/libinterp/parse-tree/pt-bytecode-walk.cc	Wed Sep 20 20:02:12 2023 -0400
@@ -2592,11 +2592,33 @@
   if (m_is_anon)
     {
       CHECK_NONNULL (m_anon_local_values);
+      // external frame offset 1 is used for storing internal offsets to local
+      // variables in nested anonymous funcitons, so assure there is no other use
+      // of frame offset by checking the size is one.
+      CHECK (m_code.m_unwind_data.m_external_frame_offset_to_internal.size () == 1);
 
       for (auto kv : *m_anon_local_values)
         {
           std::string name = kv.first;
-          CHECK (m_map_locals_to_slot.find (name) != m_map_locals_to_slot.end ()); // Ensure it is in the scope
+
+          // Nested anonymous functions leads to local variables that need to be stored
+          // in all outer anonymous function scopes. I.e. the local is in m_anon_local_values
+          // but not in m_map_locals_to_slot.
+          //
+          // These variables are given "fake" frame offset and data offset so they are accessable.
+          // They are fake in as much, that the scope object does not have the offsets registered
+          // and they are only lookup-able by name. But the resulting symbol_record (with the fake offsets)
+          // is usable. E.g. tree_evaluator::evaluate_anon_fcn_handle() needs this.
+          if (m_map_locals_to_slot.find (name) == m_map_locals_to_slot.end ())
+            {
+              int slot_added = add_id_to_table (name);
+
+              // Fake frame offset of one
+              m_code.m_unwind_data.m_external_frame_offset_to_internal.resize (2);
+              // Increasing number for fake external offset
+              int fake_external_offset = m_code.m_unwind_data.m_external_frame_offset_to_internal[1].size ();
+              m_code.m_unwind_data.m_external_frame_offset_to_internal[1][fake_external_offset] = slot_added;
+            }
 
           int slot = SLOT (name);
 
--- a/test/compile/bytecode_anon_handles.m	Wed Sep 20 20:01:27 2023 -0400
+++ b/test/compile/bytecode_anon_handles.m	Wed Sep 20 20:02:12 2023 -0400
@@ -100,6 +100,13 @@
   assert (a == 0)
   [~, a] = h2 ([-2 5])
   assert (a == 0)
+
+  % Nested anon functions
+  h1 = @(y) y * 2;
+  h2 = @(yy) execute_handle (@(yyy) h1 (yyy), yy); %h1 captured here
+  assert (h2 (3) == 6)
+  h2 = @(yy) execute_handle (@(yyyy) execute_handle (@(yyy) h1 (yyy), yyyy), yy); % Nest some more
+  assert (h2 (3) == 6)
 end
 
 function [x, y, z] = try_isargout ()
@@ -123,3 +130,7 @@
     varargout{i} = i;
   end
 end
+
+function b = execute_handle (h, arg1)
+  b = h (arg1);
+end
\ No newline at end of file