# HG changeset patch # User John W. Eaton and Fernando Alvarruiz # Date 1600699518 14400 # Mon Sep 21 10:45:18 2020 -0400 # Branch stable # Node ID 443add3fa72b1c2681598bfe0977880e1420debd # Parent 8e8cfb6304e141b37b101b2102242dfcaa9d40d0 new end refactor diff diff --git a/libinterp/parse-tree/oct-lvalue.cc b/libinterp/parse-tree/oct-lvalue.cc --- a/libinterp/parse-tree/oct-lvalue.cc +++ b/libinterp/parse-tree/oct-lvalue.cc @@ -28,6 +28,7 @@ #endif #include "error.h" +#include "errwarn.h" #include "ovl.h" #include "oct-lvalue.h" #include "ov.h" @@ -56,6 +57,115 @@ namespace octave m_frame->assign (op, m_sym, m_type, m_idx, rhs); } + octave_idx_type octave_lvalue::numel (void) const + { + // Return 1 if there is no index because without an index there + // should be no way to have a cs-list here. Cs-lists may be passed + // around internally but they are not supposed to be stored as + // single symbols in a stack frame. + + size_t num_indices = m_idx.size (); + + if (num_indices == 0) + return 1; + + switch (m_type[num_indices-1]) + { + case '(': + return 1; + + case '{': + { + // FIXME: Duplicate code in '.' case below... + + // Evaluate, skipping the last index. + + std::string tmp_type = m_type; + std::list tmp_idx = m_idx; + + tmp_type.pop_back (); + tmp_idx.pop_back (); + + octave_value tmp = eval_for_numel (tmp_type, tmp_idx); + + octave_value_list tidx = m_idx.back (); + + if (tmp.is_undefined ()) + { + if (tidx.has_magic_colon ()) + err_invalid_inquiry_subscript (); + + tmp = Cell (); + } + else if (tmp.is_zero_by_zero () + && (tmp.is_matrix_type () || tmp.is_string ())) + { + tmp = Cell (); + } + + return tmp.xnumel (tidx); + } + break; + + case '.': + { + // Evaluate, skipping either the last index or the last two + // indices if we are looking at "(idx).field". + + std::string tmp_type = m_type; + std::list tmp_idx = m_idx; + + tmp_type.pop_back (); + tmp_idx.pop_back (); + + bool paren_dot = num_indices > 1 && m_type[num_indices-2] == '('; + + // Index for paren operator, if any. + octave_value_list pidx; + + if (paren_dot) + { + pidx = tmp_idx.back (); + + tmp_type.pop_back (); + tmp_idx.pop_back (); + } + + octave_value tmp = eval_for_numel (tmp_type, tmp_idx); + + bool autoconv = (tmp.is_zero_by_zero () + && (tmp.is_matrix_type () || tmp.is_string () + || tmp.iscell ())); + + if (paren_dot) + { + // Use octave_map, not octave_scalar_map so that the + // dimensions are 0x0, not 1x1. + + if (tmp.is_undefined ()) + { + if (pidx.has_magic_colon ()) + err_invalid_inquiry_subscript (); + + tmp = octave_map (); + } + else if (autoconv) + tmp = octave_map (); + + return tmp.xnumel (pidx); + } + else if (tmp.is_undefined () || autoconv) + return 1; + else + return tmp.xnumel (octave_value_list ()); + } + break; + + default: + panic_impossible (); + } + } + void octave_lvalue::set_index (const std::string& t, const std::list& i) { @@ -105,4 +215,29 @@ namespace octave return (is_black_hole () ? octave_value () : m_frame->value (m_sym, m_type, m_idx)); } + + octave_value + octave_lvalue::eval_for_numel (const std::string& type, + const std::list& idx) const + { + octave_value retval; + + try + { + retval = m_frame->varval (m_sym); + + if (retval.is_constant () && ! idx.empty ()) + retval = retval.subsref (type, idx); + } + catch (const execution_exception&) + { + // Ignore an error and treat it as undefined. The error + // could happen because there is an index is out of range + // and we will be resizing a cell array. + + retval = octave_value (); + } + + return retval; + } } diff --git a/libinterp/parse-tree/oct-lvalue.h b/libinterp/parse-tree/oct-lvalue.h --- a/libinterp/parse-tree/oct-lvalue.h +++ b/libinterp/parse-tree/oct-lvalue.h @@ -43,7 +43,7 @@ namespace octave octave_lvalue (const symbol_record& sr, const std::shared_ptr& frame) : m_sym (sr), m_frame (frame), m_black_hole (false), - m_type (), m_idx (), m_nel (1) + m_type (), m_idx () { } octave_lvalue (const octave_lvalue&) = default; @@ -66,9 +66,7 @@ namespace octave void assign (octave_value::assign_op, const octave_value&); - void numel (octave_idx_type n) { m_nel = n; } - - octave_idx_type numel (void) const { return m_nel; } + octave_idx_type numel (void) const; void set_index (const std::string& t, const std::list& i); @@ -86,6 +84,10 @@ namespace octave private: + octave_value + eval_for_numel (const std::string& type, + const std::list& idx) const; + symbol_record m_sym; std::shared_ptr m_frame; diff --git a/libinterp/parse-tree/oct-parse.yy b/libinterp/parse-tree/oct-parse.yy --- a/libinterp/parse-tree/oct-parse.yy +++ b/libinterp/parse-tree/oct-parse.yy @@ -595,6 +595,10 @@ cell_rows : cell_or_matrix_row // tree_argument_list objects can't be empty or have leading or trailing // commas, but those are all allowed in matrix and cell array rows. +// FIXME: is tree_argument_list the best object for this purpose, or +// should we have a separate one intended specifically to represent the +// list of objects that make up elements in cell and matrix expressions? + cell_or_matrix_row : // empty { $$ = nullptr; } diff --git a/libinterp/parse-tree/pt-eval.cc b/libinterp/parse-tree/pt-eval.cc --- a/libinterp/parse-tree/pt-eval.cc +++ b/libinterp/parse-tree/pt-eval.cc @@ -36,6 +36,7 @@ #include "cmd-edit.h" #include "file-ops.h" #include "file-stat.h" +#include "lo-array-errwarn.h" #include "lo-ieee.h" #include "oct-env.h" @@ -1635,7 +1636,7 @@ namespace octave } // END is documented in op-kw-docs. -DEFCONSTMETHOD (end, interp, , , +DEFCONSTMETHOD (end, interp, args, , doc: /* -*- texinfo -*- @deftypefn {} {} end Last element of an array or the end of any @code{for}, @code{parfor}, @@ -1661,68 +1662,9 @@ Example: @seealso{for, parfor, if, do, while, function, switch, try, unwind_protect} @end deftypefn */) { - octave_value retval; - octave::tree_evaluator& tw = interp.get_evaluator (); - octave_value indexed_object = tw.indexed_object (); - int index_position = tw.index_position (); - int num_indices = tw.num_indices (); - - // If indexed_object is nullptr, then this use of 'end' is either - // appearing in a function call argument list or in an attempt to - // index an undefined symbol. There seems to be no reasonable way to - // provide a better error message. So just fail with an invalid use - // message. See bug #58830. - - if (indexed_object.is_undefined ()) - error ("invalid use of 'end': may only be used to index existing value"); - - if (indexed_object.isobject ()) - { - octave_value_list args; - - args(2) = num_indices; - args(1) = index_position + 1; - args(0) = indexed_object; - - std::string class_name = indexed_object.class_name (); - - octave::symbol_table& symtab = interp.get_symbol_table (); - - octave_value meth = symtab.find_method ("end", class_name); - - if (meth.is_defined ()) - return octave::feval (meth.function_value (), args, 1); - } - - dim_vector dv = indexed_object.dims (); - int ndims = dv.ndims (); - - if (num_indices < ndims) - { - for (int i = num_indices; i < ndims; i++) - dv(num_indices-1) *= dv(i); - - if (num_indices == 1) - { - ndims = 2; - dv.resize (ndims); - dv(1) = 1; - } - else - { - ndims = num_indices; - dv.resize (ndims); - } - } - - if (index_position < ndims) - retval = dv(index_position); - else - retval = 1; - - return retval; + return tw.evaluate_end_expression (args); } /* @@ -1740,59 +1682,31 @@ Example: namespace octave { octave_value_list - tree_evaluator::convert_to_const_vector (tree_argument_list *arg_list, - const octave_value& object) + tree_evaluator::convert_to_const_vector (tree_argument_list *args) { - // END doesn't make sense as a direct argument for a function (i.e., - // "fcn (end)" is invalid but "fcn (array (end))" is OK). Maybe we - // need a different way of asking an octave_value object this - // question? - - bool stash_object - = (object.is_defined () - && ! (object.is_function () || object.is_function_handle ())); - - unwind_protect_var upv1 (m_indexed_object); - unwind_protect_var upv2 (m_index_position); - unwind_protect_var upv3 (m_num_indices); - - if (stash_object) - m_indexed_object = object; - - int len = arg_list->length (); - - std::list args; - - auto p = arg_list->begin (); - for (int k = 0; k < len; k++) + std::list arg_vals; + + for (auto elt : *args) { - if (stash_object) - { - m_index_position = k; - m_num_indices = len; - } - - tree_expression *elt = *p++; - - if (elt) + // FIXME: is it possible for elt to be invalid? + + if (! elt) + break; + + octave_value tmp = elt->evaluate (*this); + + if (tmp.is_cs_list ()) { - octave_value tmp = elt->evaluate (*this); - - if (tmp.is_cs_list ()) - { - octave_value_list tmp_ovl = tmp.list_value (); - - for (octave_idx_type i = 0; i < tmp_ovl.length (); i++) - args.push_back (tmp_ovl(i)); - } - else if (tmp.is_defined ()) - args.push_back (tmp); + octave_value_list tmp_ovl = tmp.list_value (); + + for (octave_idx_type i = 0; i < tmp_ovl.length (); i++) + arg_vals.push_back (tmp_ovl(i)); } - else - break; + else if (tmp.is_defined ()) + arg_vals.push_back (tmp); } - return octave_value_list (args); + return octave_value_list (arg_vals); } octave_value_list @@ -3863,8 +3777,7 @@ namespace octave octave_value_list tree_evaluator::make_value_list (tree_argument_list *args, - const string_vector& arg_nm, - const octave_value& object, bool rvalue) + const string_vector& arg_nm) { octave_value_list retval; @@ -3873,10 +3786,40 @@ namespace octave unwind_protect_var *> upv (m_lvalue_list, nullptr); - if (rvalue && args->has_magic_end () && object.is_undefined ()) - err_invalid_inquiry_subscript (); - - retval = convert_to_const_vector (args, object); + int len = args->length (); + + unwind_protect_var upv2 (m_index_position); + unwind_protect_var upv3 (m_num_indices); + + m_num_indices = len; + + std::list arg_vals; + + int k = 0; + + for (auto elt : *args) + { + // FIXME: is it possible for elt to be invalid? + + if (! elt) + break; + + m_index_position = k++; + + octave_value tmp = elt->evaluate (*this); + + if (tmp.is_cs_list ()) + { + octave_value_list tmp_ovl = tmp.list_value (); + + for (octave_idx_type i = 0; i < tmp_ovl.length (); i++) + arg_vals.push_back (tmp_ovl(i)); + } + else if (tmp.is_defined ()) + arg_vals.push_back (tmp); + } + + retval = octave_value_list (arg_vals); } octave_idx_type n = retval.length (); @@ -4121,6 +4064,127 @@ namespace octave m_debugger_stack.top()->dbquit (all); } + static octave_value end_value (const octave_value& value, + octave_idx_type index_position, + octave_idx_type num_indices) + { + dim_vector dv = value.dims (); + int ndims = dv.ndims (); + + if (num_indices < ndims) + { + for (int i = num_indices; i < ndims; i++) + dv(num_indices-1) *= dv(i); + + if (num_indices == 1) + { + ndims = 2; + dv.resize (ndims); + dv(1) = 1; + } + else + { + ndims = num_indices; + dv.resize (ndims); + } + } + + return (index_position < ndims + ? octave_value (dv(index_position)) : octave_value (1.0)); + } + + octave_value_list + tree_evaluator::evaluate_end_expression (const octave_value_list& args) + { + int nargin = args.length (); + + if (nargin != 0 && nargin != 3) + print_usage (); + + if (nargin == 3) + { + octave_idx_type index_position + = args(1).xidx_type_value ("end: K must be integer value"); + + if (index_position < 1) + error ("end: K must be greater than zero"); + + octave_idx_type num_indices + = args(2).xidx_type_value ("end: N must be integer value"); + + if (num_indices < 1) + error ("end: N must be greater than zero"); + + return end_value (args(0), index_position-1, num_indices); + } + + // If m_indexed_object is undefined, then this use of 'end' is + // either appearing in a function call argument list or in an + // attempt to index an undefined symbol. There seems to be no + // reasonable way to provide a better error message. So just fail + // with an invalid use message. See bug #58830. + + if (m_indexed_object.is_undefined ()) + error ("invalid use of 'end': may only be used to index existing value"); + + octave_value expr_result; + + if (m_index_list.empty ()) + expr_result = m_indexed_object; + else + { + try + { + // End is only valid inside argument lists used for + // indexing. The dispatch class is set by the function that + // evaluates the argument list. + + // Silently ignore extra output values. + + octave_value_list tmp + = m_indexed_object.subsref (m_index_type, m_index_list, 1); + + expr_result = tmp.length () ? tmp(0) : octave_value (); + + if (expr_result.is_cs_list ()) + err_indexed_cs_list (); + } + catch (index_exception&) + { + error ("error evaluating partial expression for END"); + } + } + + if (expr_result.isobject ()) + { + // FIXME: is there a better way to lookup and execute a method + // that handles all the details like setting the dispatch class + // appropriately? + + std::string dispatch_class = expr_result.class_name (); + + symbol_table& symtab = m_interpreter.get_symbol_table (); + + octave_value meth = symtab.find_method ("end", dispatch_class); + + if (meth.is_defined ()) + { + unwind_action + act ([this] (const std::string& cls) + { + set_dispatch_class (cls); + }, get_dispatch_class ()); + + set_dispatch_class (dispatch_class); + + return m_interpreter.feval + (meth, ovl (expr_result, m_index_position+1, m_num_indices), 1); + } + } + + return end_value (expr_result, m_index_position, m_num_indices); + } + octave_value tree_evaluator::PS4 (const octave_value_list& args, int nargout) { diff --git a/libinterp/parse-tree/pt-eval.h b/libinterp/parse-tree/pt-eval.h --- a/libinterp/parse-tree/pt-eval.h +++ b/libinterp/parse-tree/pt-eval.h @@ -138,8 +138,8 @@ namespace octave m_echo_state (false), m_echo_file_name (), m_echo_file_pos (1), m_echo_files (), m_in_loop_command (false), m_breaking (0), m_continuing (0), m_returning (0), - m_indexed_object (), m_index_position (0), - m_num_indices (0) + m_indexed_object (), m_index_list (), m_index_type (), + m_index_position (0), m_num_indices (0) { } // No copying! @@ -355,9 +355,7 @@ namespace octave void undefine_parameter_list (tree_parameter_list *param_list); - octave_value_list - convert_to_const_vector (tree_argument_list *arg_list, - const octave_value& object = octave_value ()); + octave_value_list convert_to_const_vector (tree_argument_list *arg_list); octave_value_list convert_return_list_to_const_vector @@ -632,10 +630,46 @@ namespace octave return m_indexed_object; } + void set_indexed_object (const octave_value& obj = octave_value ()) + { + m_indexed_object = obj; + } + + const std::list& index_list (void) const + { + return m_index_list; + } + + void set_index_list (const std::string& index_type, + const std::list& index_list) + { + m_index_type = index_type; + m_index_list = index_list; + } + + void clear_index_list (void) + { + m_index_type = ""; + m_index_list.clear (); + } + + void append_index_list (char type, const octave_value_list& idx) + { + m_index_type += type; + m_index_list.push_back (idx); + } + + const std::string& index_type (void) const + { + return m_index_type; + } + int index_position (void) const { return m_index_position; } int num_indices (void) const { return m_num_indices; } + octave_value_list evaluate_end_expression (const octave_value_list& args); + const std::list * lvalue_list (void) const { return m_lvalue_list; @@ -704,9 +738,7 @@ namespace octave bool return_list, bool verbose = false); octave_value_list - make_value_list (tree_argument_list *args, - const string_vector& arg_nm, - const octave_value& object, bool rvalue = true); + make_value_list (tree_argument_list *args, const string_vector& arg_nm); std::list make_lvalue_list (tree_argument_list *); @@ -831,8 +863,11 @@ namespace octave // Nonzero means we're returning from a function. int m_returning; - // Used by END function. + // The following are all used by the END function. Maybe they + // should be kept together in a separate object? octave_value m_indexed_object; + std::list m_index_list; + std::string m_index_type; int m_index_position; int m_num_indices; }; diff --git a/libinterp/parse-tree/pt-id.cc b/libinterp/parse-tree/pt-id.cc --- a/libinterp/parse-tree/pt-id.cc +++ b/libinterp/parse-tree/pt-id.cc @@ -127,7 +127,10 @@ namespace octave if (fcn && ! (is_postfix_indexed () && fcn->accepts_postfix_index (postfix_index ()))) { - retval = fcn->call (tw, nargout); + if (name () == "end") + retval = tw.evaluate_end_expression (octave_value_list ()); + else + retval = fcn->call (tw, nargout); } else { diff --git a/libinterp/parse-tree/pt-idx.cc b/libinterp/parse-tree/pt-idx.cc --- a/libinterp/parse-tree/pt-idx.cc +++ b/libinterp/parse-tree/pt-idx.cc @@ -147,40 +147,6 @@ namespace octave return m_expr->name (); } - static inline octave_value_list - make_value_list (tree_evaluator& tw, - tree_argument_list *m_args, - const string_vector& m_arg_nm, const octave_value& object, - bool rvalue = true) - { - // FIXME: This function duplicates tree_evaluator::make_value_list. - // See also the way convert_to_const_vector is used in - // tree_index_expression::evaluate_n. - - octave_value_list retval; - - if (m_args) - { - unwind_action act ([&tw] (const std::list *lvl) - { - tw.set_lvalue_list (lvl); - }, tw.lvalue_list ()); - tw.set_lvalue_list (nullptr); - - if (rvalue && m_args->has_magic_end () && object.is_undefined ()) - err_invalid_inquiry_subscript (); - - retval = tw.convert_to_const_vector (m_args, object); - } - - octave_idx_type n = retval.length (); - - if (n > 0) - retval.stash_name_tags (m_arg_nm); - - return retval; - } - std::string tree_index_expression::get_struct_index (tree_evaluator& tw, @@ -210,7 +176,6 @@ namespace octave tree_index_expression::lvalue (tree_evaluator& tw) { std::list idx; - std::string tmp_type; int n = m_args.size (); @@ -220,73 +185,37 @@ namespace octave octave_lvalue retval = m_expr->lvalue (tw); - octave_value tmp = retval.value (); + unwind_action + act ([&tw] (const octave_value& val, + const std::string& index_type, + const std::list& index_list) + { + tw.set_indexed_object (val); + tw.set_index_list (index_type, index_list); + }, tw.indexed_object (), tw.index_type (), tw.index_list ()); - octave_idx_type tmpi = 0; - std::list tmpidx; + tw.set_indexed_object (retval.value ()); + tw.clear_index_list (); for (int i = 0; i < n; i++) { - if (retval.numel () != 1) - err_indexed_cs_list (); - - if (tmpi < i) - { - try - { - tmp = tmp.subsref (m_type.substr (tmpi, i-tmpi), tmpidx, true); - } - catch (index_exception& e) // problems with range, invalid type etc. - { - tw.final_index_error (e, m_expr); - } - - tmpidx.clear (); - } - switch (m_type[i]) { case '(': { - octave_value_list tidx - = make_value_list (tw, *p_args, *p_arg_nm, tmp, false); - - idx.push_back (tidx); + octave_value_list tidx = tw.make_value_list (*p_args, *p_arg_nm); - if (i < n - 1) - { - if (m_type[i+1] != '.') - error ("() must be followed by . or close the index chain"); - - tmpidx.push_back (tidx); - tmpi = i+1; - } + tw.append_index_list ('(', tidx); + idx.push_back (tidx); } break; case '{': { - octave_value_list tidx - = make_value_list (tw, *p_args, *p_arg_nm, tmp, false); - - if (tmp.is_undefined ()) - { - if (tidx.has_magic_colon ()) - err_invalid_inquiry_subscript (); + octave_value_list tidx = tw.make_value_list (*p_args, *p_arg_nm); - tmp = Cell (); - } - else if (tmp.is_zero_by_zero () - && (tmp.is_matrix_type () || tmp.is_string ())) - { - tmp = Cell (); - } - - retval.numel (tmp.xnumel (tidx)); - + tw.append_index_list ('{', tidx); idx.push_back (tidx); - tmpidx.push_back (tidx); - tmpi = i; } break; @@ -294,47 +223,7 @@ namespace octave { octave_value tidx = get_struct_index (tw, p_arg_nm, p_dyn_field); - bool autoconv = (tmp.is_zero_by_zero () - && (tmp.is_matrix_type () || tmp.is_string () - || tmp.iscell ())); - - if (i > 0 && m_type[i-1] == '(') - { - octave_value_list pidx = idx.back (); - - // Use octave_map, not octave_scalar_map so that the - // dimensions are 0x0, not 1x1. - if (tmp.is_undefined ()) - { - if (pidx.has_magic_colon ()) - err_invalid_inquiry_subscript (); - - tmp = octave_map (); - } - else if (autoconv) - tmp = octave_map (); - - retval.numel (tmp.xnumel (pidx)); - - tmpi = i-1; - tmpidx.push_back (tidx); - } - else - { - if (tmp.is_undefined () || autoconv) - { - tmpi = i+1; - tmp = octave_value (); - } - else - { - retval.numel (tmp.xnumel (octave_value_list ())); - - tmpi = i; - tmpidx.push_back (tidx); - } - } - + tw.append_index_list ('.', tidx); idx.push_back (tidx); } break; @@ -573,102 +462,127 @@ namespace octave // files in a +packagename folder, they will also be an // octave_classdef_meta object, but we don't want to index them. - bool indexing_object = (base_expr_val.isobject () - || base_expr_val.isjava () - || (base_expr_val.is_classdef_meta () - && ! base_expr_val.is_package ())); - std::list idx_list; - octave_value partial_expr_val = base_expr_val; - - for (int i = beg; i < n; i++) - { - if (i > beg) - { - tree_argument_list *al = *p_args; + { + // Note: need new scope so that the following unwind action will + // happen before we perform the final indexing for objects (for + // example). - if (! indexing_object || (al && al->has_magic_end ())) - { - // Evaluate what we have so far to find the value to - // pass to the END function. + unwind_action + act ([&tw] (const octave_value& val, + const std::string& index_type, + const std::list& index_list) + { + tw.set_indexed_object (val); + tw.set_index_list (index_type, index_list); + }, + tw.indexed_object (), + tw.index_type (), tw.index_list ()); - try - { - // Silently ignore extra output values. + tw.set_indexed_object (); + tw.clear_index_list (); - octave_value_list tmp_list - = base_expr_val.subsref (m_type.substr (beg, i-beg), - idx_list, nargout); + bool indexing_object = (base_expr_val.isobject () + || base_expr_val.isjava () + || (base_expr_val.is_classdef_meta () + && ! base_expr_val.is_package ())); + + octave_value partial_expr_val = base_expr_val; - partial_expr_val - = tmp_list.length () ? tmp_list(0) : octave_value (); + for (int i = beg; i < n; i++) + { + if (i > beg) + { + if (! indexing_object) + { + // Evaluate what we have so far. - if (! indexing_object) - { - base_expr_val = partial_expr_val; - - if (partial_expr_val.is_cs_list ()) - err_indexed_cs_list (); - - retval = partial_expr_val; + try + { + // Silently ignore extra output values. - beg = i; - idx_list.clear (); + octave_value_list tmp_list + = base_expr_val.subsref (m_type.substr (beg, i-beg), + idx_list, nargout); + + partial_expr_val + = tmp_list.length () ? tmp_list(0) : octave_value (); + + base_expr_val = partial_expr_val; - if (partial_expr_val.isobject () - || partial_expr_val.isjava () - || (partial_expr_val.is_classdef_meta () - && ! partial_expr_val.is_package ())) - { - // Found an object, so now we'll build up - // complete index list for one big subsref - // call from this point on. + if (partial_expr_val.is_cs_list ()) + err_indexed_cs_list (); + + retval = partial_expr_val; + + beg = i; + idx_list.clear (); + tw.clear_index_list (); - // FIXME: is is also possible to have a - // static method call buried somewhere in - // the depths of a complex indexing - // expression so that we would also need to - // check for an octave_classdef_meta object - // here? + if (partial_expr_val.isobject () + || partial_expr_val.isjava () + || (partial_expr_val.is_classdef_meta () + && ! partial_expr_val.is_package ())) + { + // Found an object, so now we'll build up + // complete index list for one big subsref + // call from this point on. - indexing_object = true; - } - } - } - catch (index_exception& e) - { - tw.final_index_error (e, m_expr); - } - } - } + // FIXME: is is also possible to have a + // static method call buried somewhere in + // the depths of a complex indexing + // expression so that we would also need to + // check for an octave_classdef_meta object + // here? + + indexing_object = true; + } + } + catch (index_exception& e) + { + tw.final_index_error (e, m_expr); + } + } + } + + tw.set_indexed_object (partial_expr_val); - switch (m_type[i]) - { - case '(': - idx_list.push_back (make_value_list (tw, *p_args, *p_arg_nm, - partial_expr_val)); - break; + switch (m_type[i]) + { + case '(': + { + octave_value_list tmp = tw.make_value_list (*p_args, *p_arg_nm); + tw.append_index_list ('(', tmp); + idx_list.push_back (tmp); + } + break; - case '{': - idx_list.push_back (make_value_list (tw, *p_args, *p_arg_nm, - partial_expr_val)); - break; + case '{': + { + octave_value_list tmp = tw.make_value_list (*p_args, *p_arg_nm); + tw.append_index_list ('{', tmp); + idx_list.push_back (tmp); + } + break; - case '.': - idx_list.push_back (octave_value - (get_struct_index (tw, p_arg_nm, p_dyn_field))); - break; + case '.': + { + octave_value tmp = get_struct_index (tw, p_arg_nm, p_dyn_field); + tw.append_index_list ('.', tmp); + idx_list.push_back (tmp); + } + break; - default: - panic_impossible (); - } + default: + panic_impossible (); + } - p_args++; - p_arg_nm++; - p_dyn_field++; - } - + p_args++; + p_arg_nm++; + p_dyn_field++; + } + } // If ! idx_list.empty () that means we still have stuff to index // otherwise they would have been dealt with and idx_list would have @@ -760,16 +674,11 @@ namespace octave } } - // Delete any temporary values prior to pushing the result and - // returning so that destructors for any temporary classdef handle - // objects will be called before we return. Otherwise, the - // destructor may push result values that will wipe out the result - // that we push below. Although the method name is "push_result" - // there is only a single register (either an octave_value or an - // octave_value_list) not a stack. + // Delete any temporary values prior to returning so that + // destructors for any temporary classdef handle objects will be + // called before we return. idx_list.clear (); - partial_expr_val = octave_value (); base_expr_val = octave_value (); val = octave_value (); diff --git a/test/bug-58593/bug-58593.tst b/test/bug-58593/bug-58593.tst new file mode 100644 --- /dev/null +++ b/test/bug-58593/bug-58593.tst @@ -0,0 +1,40 @@ +######################################################################## +## +## Copyright (C) 2017-2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## 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 +## . +## +######################################################################## + +%!test <*58593> +%! obj = myclass1 (); +%! assert (obj.data (plus (minus (end,1), 1)), 1005) + +%!test <*58593> +%! obj = myclass2 (); +%! assert (obj.alldata, 1001:1005) + +%!test <*58593> +%! obj = myclass2 (); +%! assert (obj(end), 1004) + +%!test <*58593> +%! obj = myclass2 (); +%! assert (obj.data(end), 1005) diff --git a/test/bug-58593/module.mk b/test/bug-58593/module.mk new file mode 100644 --- /dev/null +++ b/test/bug-58593/module.mk @@ -0,0 +1,6 @@ +bug_58593_TEST_FILES = \ + %reldir%/bug-58593.tst \ + %reldir%/myclass1.m \ + %reldir%/myclass2.m + +TEST_FILES += $(bug_58593_TEST_FILES) diff --git a/test/bug-58593/myclass1.m b/test/bug-58593/myclass1.m new file mode 100644 --- /dev/null +++ b/test/bug-58593/myclass1.m @@ -0,0 +1,10 @@ +classdef myclass1 < handle + properties + data + end + methods + function obj = myclass1 () + obj.data = 1001:1005; + end + end +end diff --git a/test/bug-58593/myclass2.m b/test/bug-58593/myclass2.m new file mode 100644 --- /dev/null +++ b/test/bug-58593/myclass2.m @@ -0,0 +1,40 @@ +classdef myclass2 < handle + properties + data_ + end + + methods + function obj = myclass2(data) + obj.data_ = 1001:1005; + end + + function r = subsref(obj,S) + switch S(1).type + case '.' + switch S(1).subs + case 'data' + % Transform: obj.data --> obj.data_ + r = subsref (obj.data_, S(2:end)); + case 'alldata' + % Transform: obj.data --> obj.data_(1:end) + % This expression should trigger *builtin* subsref *twice* (one + % for the evaluation of 'end', and the other for the whole rvalue). + % 'end' here is also builtin 'end' + r = obj.data_(1:end); + otherwise + error('Incorrect usage') + end + case '()' + % Transform: obj(index) --> obj.data_(1:end) + r = subsref(obj.data_,S); + otherwise + error('Incorrect usage') + end + end + + function r = end(obj,k,n) + % We subtract 1 from the "real" end of obj.data_ + r = builtin('end',obj.data_,k,n)-1; + end + end +end diff --git a/test/module.mk b/test/module.mk --- a/test/module.mk +++ b/test/module.mk @@ -84,6 +84,7 @@ include %reldir%/bug-53956/module.mk include %reldir%/bug-54995/module.mk include %reldir%/bug-55758/module.mk include %reldir%/bug-58572/module.mk +include %reldir%/bug-58593/module.mk include %reldir%/class-concat/module.mk include %reldir%/classdef/module.mk include %reldir%/classdef-multiple-inheritance/module.mk diff --git a/test/struct.tst b/test/struct.tst --- a/test/struct.tst +++ b/test/struct.tst @@ -155,9 +155,13 @@ %! assert ({b.name}, {1, 2, "b", "e", 3, 4}); %! assert ({b.value}, {100, "5", 100, "6", 100, "7"}); +%!error +%! [a(1:3).x] = deal ([1, 5], [3, 7], [8, 9]); +%! [a(2:3).x(2)] = deal (10, 11); + %!error %! [a(1:3).x] = deal ([1, 5], [3, 7], [8, 9]); -%! [a(2:3).x(2)] = deal (10, 11); +%! [a(2:3).x(2)] = 1; %!error %! [a(1:3).x] = deal ([1, 5], [3, 7], [8, 9]);