Mercurial > octave
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