# HG changeset patch # User John W. Eaton # Date 1595566895 14400 # Node ID e70c859c4bff777cb588ea42776994d62faa9360 # Parent c83e0c85038656e8aacdfb4790033eae98ab1bc8 allow infinite limits on for loop ranges (bug #45143) * ov.cc (octave_value::octave_value (const Range&, bool)): If force_range is false, throw error if range is not OK. * Range.h, Range.cc (Range::ok): New function. (Range::numel_internal): Return one less than the maximum possible octave_idx_type value if infinite range is detected. (Range::init): Don't set m_limit if limit is infinite. (Range::Range (double, double)): Likewise. (Range::Range (double, double, double)): Likewise. (Range::Range (double, double, octave_idx_type): Trust supplied value of numel. * for.tst: New tests. * pt-eval.cc (tree_evaluator::visit_simple_for_command): Warn if range limit is infinite. diff -r c83e0c850386 -r e70c859c4bff libinterp/octave-value/ov.cc --- a/libinterp/octave-value/ov.cc Tue Jul 21 17:05:15 2020 -0400 +++ b/libinterp/octave-value/ov.cc Fri Jul 24 01:01:35 2020 -0400 @@ -1075,10 +1075,16 @@ } octave_value::octave_value (const Range& r, bool force_range) - : rep (force_range || ! Vdisable_range - ? dynamic_cast (new octave_range (r)) - : dynamic_cast (new octave_matrix (r.matrix_value ()))) + : rep (nullptr) { + if (! force_range && ! r.ok ()) + error ("invalid range"); + + if (force_range || ! Vdisable_range) + rep = dynamic_cast (new octave_range (r)); + else + rep = dynamic_cast (new octave_matrix (r.matrix_value ())); + maybe_mutate (); } diff -r c83e0c850386 -r e70c859c4bff libinterp/parse-tree/pt-eval.cc --- a/libinterp/parse-tree/pt-eval.cc Tue Jul 21 17:05:15 2020 -0400 +++ b/libinterp/parse-tree/pt-eval.cc Fri Jul 24 01:01:35 2020 -0400 @@ -2529,6 +2529,10 @@ octave_idx_type steps = rng.numel (); + if (octave::math::isinf (rng.limit ())) + warning ("FOR loop limit is infinite, will stop after %ld steps", + steps); + for (octave_idx_type i = 0; i < steps; i++) { if (m_echo_state) diff -r c83e0c850386 -r e70c859c4bff liboctave/array/Range.cc --- a/liboctave/array/Range.cc Tue Jul 21 17:05:15 2020 -0400 +++ b/liboctave/array/Range.cc Fri Jul 24 01:01:35 2020 -0400 @@ -527,9 +527,16 @@ { octave_idx_type retval = -1; - if (m_inc == 0 - || (m_limit > m_base && m_inc < 0) - || (m_limit < m_base && m_inc > 0)) + if (! octave::math::isfinite (m_base) || ! octave::math::isfinite (m_inc) + || octave::math::isnan (m_limit)) + retval = -2; + else if (octave::math::isinf (m_limit) + && ((m_inc > 0 && m_limit > 0) + || (m_inc < 0 && m_limit < 0))) + retval = std::numeric_limits::max () - 1; + else if (m_inc == 0 + || (m_limit > m_base && m_inc < 0) + || (m_limit < m_base && m_inc > 0)) { retval = 0; } @@ -539,8 +546,8 @@ double tmp = tfloor ((m_limit - m_base + m_inc) / m_inc, ct); - octave_idx_type n_elt = (tmp > 0.0 ? static_cast (tmp) - : 0); + octave_idx_type n_elt = (tmp > 0.0 + ? static_cast (tmp) : 0); // If the final element that we would compute for the range is equal to // the limit of the range, or is an adjacent floating point number, @@ -559,8 +566,8 @@ n_elt++; } - retval = (n_elt < std::numeric_limits::max () - 1) - ? n_elt : -1; + retval = ((n_elt < std::numeric_limits::max ()) + ? n_elt : -1); } return retval; @@ -569,12 +576,7 @@ double Range::limit_internal (void) const { - double new_limit; - - if (m_inc > 0) - new_limit = max (); - else - new_limit = min (); + double new_limit = m_inc > 0 ? max () : min (); // If result must be an integer then force the new_limit to be one. if (all_elements_are_ints ()) @@ -587,5 +589,7 @@ Range::init (void) { m_numel = numel_internal (); - m_limit = limit_internal (); + + if (! octave::math::isinf (m_limit)) + m_limit = limit_internal (); } diff -r c83e0c850386 -r e70c859c4bff liboctave/array/Range.h --- a/liboctave/array/Range.h Tue Jul 21 17:05:15 2020 -0400 +++ b/liboctave/array/Range.h Fri Jul 24 01:01:35 2020 -0400 @@ -53,13 +53,15 @@ Range (double b, double l) : m_base (b), m_limit (l), m_inc (1), m_numel (numel_internal ()) { - m_limit = limit_internal (); + if (! octave::math::isinf (m_limit)) + m_limit = limit_internal (); } Range (double b, double l, double i) : m_base (b), m_limit (l), m_inc (i), m_numel (numel_internal ()) { - m_limit = limit_internal (); + if (! octave::math::isinf (m_limit)) + m_limit = limit_internal (); } // NOTE: The following constructor may be deprecated and removed after @@ -70,16 +72,15 @@ Range (double b, double i, octave_idx_type n) : m_base (b), m_limit (b + (n-1) * i), m_inc (i), m_numel (n) { - if (! octave::math::isfinite (b) || ! octave::math::isfinite (i) - || ! octave::math::isfinite (m_limit)) - m_numel = -2; - else - { - // Code below is only needed if the resulting range must be 100% - // correctly constructed. If the Range object created is only - // a temporary one used by operators this may be unnecessary. - m_limit = limit_internal (); - } + if (! octave::math::isinf (m_limit)) + m_limit = limit_internal (); + } + + // The range has a finite number of elements. + bool ok (void) const + { + return (octave::math::isfinite (m_limit) + && (m_numel >= 0 || m_numel == -2)); } double base (void) const { return m_base; } @@ -172,11 +173,7 @@ // For operators' usage (to allow all values to be set directly). Range (double b, double l, double i, octave_idx_type n) : m_base (b), m_limit (l), m_inc (i), m_numel (n) - { - if (! octave::math::isfinite (b) || ! octave::math::isfinite (i) - || ! octave::math::isfinite (l)) - m_numel = -2; - } + { } }; OCTAVE_DEPRECATED (7, "arithmetic operations on Range objects are unreliable") diff -r c83e0c850386 -r e70c859c4bff test/for.tst --- a/test/for.tst Tue Jul 21 17:05:15 2020 -0400 +++ b/test/for.tst Fri Jul 24 01:01:35 2020 -0400 @@ -161,3 +161,20 @@ %! endfor %! assert (cnt, 0); %! assert (k, cell (0,3)); + +%!test <45143> +%! fail ("for i = 0:inf; break; end", "warning", +%! "FOR loop limit is infinite"); +%! +%! fail ("for i = 0:-1:-inf; break; end", "warning", +%! "FOR loop limit is infinite"); + +%!test <45143> +%! k = 0; for i = 1:inf; if (++k > 10); break; end; end +%! assert (i, 11); +%! k = 0; for i = -1:-1:-inf; if (++k > 10); break; end; end +%! assert (i, -11); +%! k = 0; for i = 1:-inf; if (++k > 10); break; end; end +%! assert (i, zeros(1,0)); +%! k = 0; for i = 0:-1:inf; if (++k > 10); break; end; end +%! assert (i, zeros(1,0));