Mercurial > octave
changeset 21990:efce657ceb86
Provide a warning when an array is used in an if/while/until (bug #43098).
* errwarn.{cc,h} (warn_array_as_logical,
reset_first_array_as_logical_since_keyboard): New functions
* error.cc (initialize_default_warning_state):
Disable Octave:array-as-logical by default
* warning_ids.m: Add new warning Octave:array-as-logical.
* input.cc (interactive_input):
Reset first_array_as_logical_since_keyboard.
* ov-base-mat.cc (octave_base_matrix<MT>::is_true):
Call warn_array_as_logical if > 1x1.
* ov-base-diag.cc (octave_base_diag<DMT, MT>::is_true):
Call warn_array_as_logical if > 1x1. Optimise calculation.
* ov-perm.cc (octave_perm_matrix::do_index_op):
Call warn_array_as_logical if > 1x1. Optimise calculation.
* ov-range.cc (octave_range::is_true):
Call warn_array_as_logical if > 1x1. Optimise calculation.
* ov-base-sparse (octave_base_sparse<T>::is_true)
Call warn_array_as_logical if > 1x1. Check for NaN/NA.
* Sparse.cc (Sparse<T>::SparseRep::any_element_is_nan): New function.
* Sparse.h (Sparse<T>::SparseRep::any_element_is_nan,
Sparse<T>::any_element_is_nan): New functions.
* Sparse-b.cc (Sparse<bool>::SparseRep::any_element_is_nan):
New function, specialization of the above.
* if.tst: New built-in self tests.
author | Lachlan Andrew <lachlanbis@gmail.com> |
---|---|
date | Wed, 29 Jun 2016 08:50:09 -0700 |
parents | 6bce4d23af6b |
children | 80659e58609f |
files | libinterp/corefcn/error.cc libinterp/corefcn/errwarn.cc libinterp/corefcn/errwarn.h libinterp/octave-value/ov-base-diag.cc libinterp/octave-value/ov-base-mat.cc libinterp/octave-value/ov-base-sparse.cc libinterp/octave-value/ov-perm.cc libinterp/octave-value/ov-range.cc libinterp/octave-value/ov.cc liboctave/array/Sparse-b.cc liboctave/array/Sparse.cc liboctave/array/Sparse.h scripts/help/warning_ids.m test/if.tst |
diffstat | 14 files changed, 153 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/error.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/corefcn/error.cc Wed Jun 29 08:50:09 2016 -0700 @@ -1780,6 +1780,7 @@ // Most people will want to have the following disabled. + disable_warning ("Octave:array-as-logical"); disable_warning ("Octave:array-to-scalar"); disable_warning ("Octave:array-to-vector"); disable_warning ("Octave:imag-to-real");
--- a/libinterp/corefcn/errwarn.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/corefcn/errwarn.cc Wed Jun 29 08:50:09 2016 -0700 @@ -273,6 +273,23 @@ } void +warn_array_as_logical (const dim_vector& dv) +{ + warning_with_id ("Octave:array-as-logical", + "Using an object of size %s as " + "a boolean value implies all().", + dv.str ().c_str ()); +} + +/* +%!warning <boolean value implies all> +%! warning ("on", "Octave:array-as-logical"); +%! if ([1 1 0]) +%! assert (false); +%! endif +*/ + +void warn_complex_cmp (void) { warning_with_id ("Octave:language-extension",
--- a/libinterp/corefcn/errwarn.h Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/corefcn/errwarn.h Wed Jun 29 08:50:09 2016 -0700 @@ -145,6 +145,9 @@ err_wrong_type_arg_for_unary_op (const octave_value& op); OCTINTERP_API extern void +warn_array_as_logical (const dim_vector& dv); + +OCTINTERP_API extern void warn_complex_cmp (void); OCTINTERP_API extern void
--- a/libinterp/octave-value/ov-base-diag.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/octave-value/ov-base-diag.cc Wed Jun 29 08:50:09 2016 -0700 @@ -273,11 +273,20 @@ return retval; } +// Return true if this matrix has all true elements (non-zero, not NA/NaN). template <typename DMT, typename MT> bool octave_base_diag<DMT, MT>::is_true (void) const { - return to_dense ().is_true (); + if (dims ().numel () > 1) + { + warn_array_as_logical (dims ()); + // Throw error if any NaN or NA by calling is_true(). + octave_value (matrix.extract_diag ()).is_true (); + return false; // > 1x1 diagonal always has zeros + } + else + return to_dense ().is_true (); // 0x0 or 1x1, handle NaN etc. } // FIXME: This should be achieveable using ::real
--- a/libinterp/octave-value/ov-base-mat.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/octave-value/ov-base-mat.cc Wed Jun 29 08:50:09 2016 -0700 @@ -30,6 +30,7 @@ #include "Array-util.h" #include "Cell.h" +#include "errwarn.h" #include "ovl.h" #include "oct-map.h" #include "ov-base.h" @@ -401,6 +402,7 @@ return retval; } +// Return true if this matrix has all true elements (non-zero, not NA/NaN). template <typename MT> bool octave_base_matrix<MT>::is_true (void) const @@ -416,6 +418,9 @@ if (t1.any_element_is_nan ()) err_nan_to_logical_conversion (); + if (nel > 1) + warn_array_as_logical (dv); + boolNDArray t2 = t1.all (); retval = t2(0);
--- a/libinterp/octave-value/ov-base-sparse.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/octave-value/ov-base-sparse.cc Wed Jun 29 08:50:09 2016 -0700 @@ -300,13 +300,22 @@ octave_idx_type nel = dv.numel (); octave_idx_type nz = nnz (); - if (nz == nel && nel > 0) + if (nel > 0) { T t1 (matrix.reshape (dim_vector (nel, 1))); - SparseBoolMatrix t2 = t1.all (); + if (t1.any_element_is_nan ()) + err_nan_to_logical_conversion (); + + if (nel > 1) + warn_array_as_logical (dv); - retval = t2(0); + if (nz == nel) + { + SparseBoolMatrix t2 = t1.all (); + + retval = t2(0); + } } return retval;
--- a/libinterp/octave-value/ov-perm.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/octave-value/ov-perm.cc Wed Jun 29 08:50:09 2016 -0700 @@ -131,10 +131,18 @@ return retval; } +// Return true if this matrix has all true elements (non-zero, not NaN/NA). +// A permutation cannot have NaN/NA. bool octave_perm_matrix::is_true (void) const { - return to_dense ().is_true (); + if (dims ().numel () > 1) + { + warn_array_as_logical (dims ()); + return false; // > 1x1 permutation always has zeros, and no NaN. + } + else + return dims ().numel (); // 1x1 is [1] == true, 0x0 == false. } double
--- a/libinterp/octave-value/ov-range.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/octave-value/ov-range.cc Wed Jun 29 08:50:09 2016 -0700 @@ -264,6 +264,8 @@ return mat.diag (m, n); } +// Return true if this range has all true elements (non-zero, not NaN/NA). +// A range cannot have NaN/NA. bool octave_range::is_true (void) const { @@ -271,10 +273,30 @@ if (! range.is_empty ()) { - // FIXME: this is a potential waste of memory. - Matrix m ((range.matrix_value ().all ()).all ()); + if (dims ().numel () > 1) + warn_array_as_logical (dims ()); + + Range r = range_value (); + double base = r.base (); + double limit = r.limit (); - retval = (m.rows () == 1 && m.columns () == 1 && m (0, 0) != 0.0); + // Can't be zero if we start and finish on the same size of 0 + if (((base > 0 && limit > 0) || (base < 0 && limit < 0)) && numel () > 0) + retval = true; + else + { + /* + // This tells us whether one element is 0, if arithmetic is exact. + double steps_to_zero = base / r.inc (); + + retval = (steps_to_zero != floor (steps_to_zero)); + */ + + // FIXME: this is a waste of memory. + Matrix m ((range.matrix_value ().all ()).all ()); + + retval = ! m.is_empty () && m(0, 0) != 0.0; + } } return retval;
--- a/libinterp/octave-value/ov.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/libinterp/octave-value/ov.cc Wed Jun 29 08:50:09 2016 -0700 @@ -1411,7 +1411,17 @@ // Empty array also means a match. if (tmp.is_defined ()) - retval = tmp.is_true () || tmp.is_empty (); + { + if (tmp.is_empty ()) + retval = true; + else + { + // Reshape into a vector and call all() explicitly, + // to avoid Octave:array-as-logical warning. + tmp = tmp.reshape (dim_vector (tmp.numel (), 1)); + retval = tmp.all ().is_true (); + } + } } return retval;
--- a/liboctave/array/Sparse-b.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/liboctave/array/Sparse-b.cc Wed Jun 29 08:50:09 2016 -0700 @@ -30,6 +30,13 @@ #include "Sparse.h" #include "Sparse.cc" +template <> +bool +Sparse<bool>::SparseRep::any_element_is_nan (void) const +{ + return false; +} + INSTANTIATE_SPARSE (bool, OCTAVE_API); #if 0
--- a/liboctave/array/Sparse.cc Tue Jun 28 16:42:15 2016 -0400 +++ b/liboctave/array/Sparse.cc Wed Jun 29 08:50:09 2016 -0700 @@ -193,6 +193,19 @@ } template <typename T> +bool +Sparse<T>::SparseRep::any_element_is_nan (void) const +{ + octave_idx_type nz = nnz (); + + for (octave_idx_type i = 0; i < nz; i++) + if (octave::math::isnan (d[i])) + return true; + + return false; +} + +template <typename T> Sparse<T>::Sparse (octave_idx_type nr, octave_idx_type nc, T val) : rep (0), dimensions (dim_vector (nr, nc)) {
--- a/liboctave/array/Sparse.h Tue Jun 28 16:42:15 2016 -0400 +++ b/liboctave/array/Sparse.h Wed Jun 29 08:50:09 2016 -0700 @@ -136,6 +136,8 @@ bool indices_ok (void) const; + bool any_element_is_nan (void) const; + private: // No assignment! @@ -692,6 +694,9 @@ { return map<U, U (&) (const T&)> (fcn); } bool indices_ok (void) const { return rep->indices_ok (); } + + bool any_element_is_nan (void) const + { return rep->any_element_is_nan (); } }; template <typename T>
--- a/scripts/help/warning_ids.m Tue Jun 28 16:42:15 2016 -0400 +++ b/scripts/help/warning_ids.m Wed Jun 29 08:50:09 2016 -0700 @@ -23,6 +23,12 @@ ## @item Octave:abbreviated-property-match ## By default, the @code{Octave:abbreviated-property-match} warning is enabled. ## +## @item Octave:array-as-logical +## If the @code{Octave:array-to-scalar} warning is enabled, +## Octave will warn when an array of size greater than 1x1 is used +## as a truth value in an if, while or until statement. +## By default, the @code{Octave:array-as-logical} warning is disabled. +## ## @item Octave:array-to-scalar ## If the @code{Octave:array-to-scalar} warning is enabled, Octave will ## warn when an implicit conversion from an array to a scalar value is
--- a/test/if.tst Tue Jun 28 16:42:15 2016 -0400 +++ b/test/if.tst Wed Jun 29 08:50:09 2016 -0700 @@ -99,3 +99,32 @@ %! endif %! assert (x, 13); +## test "is_true" of different data types +%!error diag (NaN) || 0; +%!test +%! d1 = diag ([]) || 0; +%! d2 = diag (1) || 0; +%! d3 = diag ([1 2]) || 0; +%! assert ([d1 d2 d3], [false true false]); + +%!error sparse (NaN) || 0; +%!error sparse ([1 1 ; 1 NaN]) || 0; +%!test +%! s1 = sparse ([]) || 0; +%! s2 = sparse (1) || 0; +%! s3 = sparse ([1 0 ; 0 2]) || 0; +%! s4 = sparse ([1 1 ; 1 1]) || 0; +%! assert ([s1 s2 s3 s4], [false true false true]); + +%!test +%! r1 = (1:10) || 0; +%! r2 = (-10:-1) || 0; +%! r3 = (-1:1) || 0; +%! assert ([r1 r2 r3], [true true false]); + +%!test +%! c1 = [2i 4i] || 0; +%! c2 = [22 4i] || 0; +%! c3 = i || 0; +%! c4 = complex(0) || 0; +%! assert ([c1 c2 c3 c4], [true true true false]);