# HG changeset patch # User Petter T. # Date 1695220814 -7200 # Wed Sep 20 16:40:14 2023 +0200 # Node ID 79156633a9d2153e3cc1a8c00306b2606fba1025 # Parent f0f93e3b3b8e8b40148be6e07456191d3d122589 VM Support nested anonymous functions that access outer locals 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 diff -r f0f93e3b3b8e -r 79156633a9d2 libinterp/parse-tree/pt-bytecode-walk.cc --- a/libinterp/parse-tree/pt-bytecode-walk.cc Wed Sep 20 13:44:41 2023 +0200 +++ b/libinterp/parse-tree/pt-bytecode-walk.cc Wed Sep 20 16:40:14 2023 +0200 @@ -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); diff -r f0f93e3b3b8e -r 79156633a9d2 test/compile/bytecode_anon_handles.m --- a/test/compile/bytecode_anon_handles.m Wed Sep 20 13:44:41 2023 +0200 +++ b/test/compile/bytecode_anon_handles.m Wed Sep 20 16:40:14 2023 +0200 @@ -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