comparison liboctave/util/lo-array-gripes.cc @ 20574:dd6345fd8a97

use exceptions for better invalid index error reporting (bug #45957) * lo-array-gripes.h, lo-array-gripes.cc (index_exception): New base class for indexing errors. (invalid_index, out_of_range): New classes. (gripe_index_out_of_range): New overloaded function. (gripe_invalid_index): New overloaded functions. Delete version with no arguments. (gripe_invalid_assignment_size, gripe_assignment_dimension_mismatch): Delete. Change uses of gripe functions as needed. * Cell.cc (Cell::index, Cell::assign, Cell::delete_elements): Use exceptions to collect error info about and handle indexing errors. * data.cc (Fnth_element, do_accumarray_sum, F__accumarray_sum__, do_accumarray_minmax, do_accumarray_minmax_fun, F__accumdim_sum__): Likewise. * oct-map.cc (octave_map::index, octave_map::assign, octave_map::delete_elements): Likewise. * sparse.cc (Fsparse): Likewise. * sub2ind.cc (Fsub2ind, Find2sub): Likewise. New tests. * utils.cc (dims_to_numel): Likewise. * ov-base-diag.cc (octave_base_diag<DMT, MT>::do_index_op, octave_base_diag<DMT, MT>::subsasgn): Likewise. * ov-base-mat.cc (octave_base_matrix<MT>::subsref, octave_base_matrix<MT>::assign): Likewise. * ov-base-sparse.cc (octave_base_sparse<T>::do_index_op, octave_base_sparse<T>::assign, octave_base_sparse<MT>::delete_elements): Likewise. * ov-classdef.cc (cdef_object_array::subsref, cdef_object_array::subsasgn): Likewise. * ov-java.cc (make_java_index): Likewise. * ov-perm.cc (octave_perm_matrix::do_index_op): Likewise. * ov-range.cc (octave_range::do_index_op): Likewise. * ov-re-diag.cc (octave_diag_matrix::do_index_op): Likewise. * ov-str-mat.cc (octave_char_matrix_str::do_index_op_internal): Likewise. * pt-assign.cc (tree_simple_assignment::rvalue1): Likewise. * pt-idx.cc (tree_index_expression::rvalue, tree_index_expression::lvalue): Likewise. * Array-util.cc (sub2ind): Likewise. * toplev.cc (main_loop): Also catch unhandled index_exception exceptions. * ov-base.cc (octave_base_value::index_vector): Improve error message. * ov-re-sparse.cc (octave_sparse_matrix::index_vector): Likewise. * ov-complex.cc (complex_index): New class. (gripe_complex_index): New function. (octave_complex::index_vector): Use it. * pt-id.h, pt-id.cc (tree_identifier::is_variable, tree_black_hole::is_variable): Now const. * pt-idx.cc (final_index_error): New static function. (tree_index_expression::rvalue, tree_index_expression::lvalue): Use it. * index.tst: New tests.
author Lachlan Andrew <lachlanbis@gmail.com>
date Fri, 02 Oct 2015 15:07:37 -0400
parents 4197fc428c7d
children e54ecb33727e
comparison
equal deleted inserted replaced
20573:e3c0fee87493 20574:dd6345fd8a97
23 23
24 #ifdef HAVE_CONFIG_H 24 #ifdef HAVE_CONFIG_H
25 #include <config.h> 25 #include <config.h>
26 #endif 26 #endif
27 27
28 #include <string.h>
28 #include "lo-array-gripes.h" 29 #include "lo-array-gripes.h"
29 #include "lo-error.h" 30 #include "lo-error.h"
30 31
31 const char *error_id_nonconformant_args = "Octave:nonconformant-args"; 32 const char *error_id_nonconformant_args = "Octave:nonconformant-args";
32 33
88 (err_id, "%s: nonconformant arguments (op1 is %s, op2 is %s)", 89 (err_id, "%s: nonconformant arguments (op1 is %s, op2 is %s)",
89 op, op1_dims_str.c_str (), op2_dims_str.c_str ()); 90 op, op1_dims_str.c_str (), op2_dims_str.c_str ());
90 } 91 }
91 92
92 void 93 void
94 gripe_del_index_out_of_range (bool is1d, octave_idx_type idx,
95 octave_idx_type ext)
96 {
97 const char *err_id = error_id_index_out_of_bounds;
98
99 (*current_liboctave_error_with_id_handler)
100 (err_id, "A(%s) = []: index out of bounds; value %d out of bound %d",
101 is1d ? "I" : "..,I,..", idx, ext);
102 }
103
104
105
106 // Common procedures of base class index_exception, thrown whenever an
107 // object is indexed incorrectly, such as by an index that is out of
108 // range, negative, fractional, complex, or of a non-numeric type.
109
110 const char *
111 index_exception::err (void) throw ()
112 {
113 msg = access () + "; " + explain ();
114 return msg.c_str ();
115 }
116
117 // Show what was illegally accessed, e.g., "A(-1,_)", "A(0+1i)", "A(_,3)"
118 // Show how many indices come before/after the offending one,
119 // e.g., (<error>), (<error>,_), or (_,<error>,...[x5]...)
120
121 std::string
122 index_exception:: access (void) const
123 {
124 // FIXME: don't use a fixed size buffer!
125
126 int buf_len = 300;
127
128 char output [buf_len];
129 char pre [buf_len];
130 char post [buf_len];
131
132 // dim == 0 if position not yet given, or
133 // <static_cast unsigned int>(-1) if explicitly shown to be unknown
134 // both are caught by this condition
135
136 if (static_cast <unsigned int> (dim-1) > 100000)
137 {
138 // No parentheses are given if the dimension is not known.
139 pre[0] = post[0] = '\0';
140 }
141 else
142 {
143 if (dim < 5)
144 {
145 pre[0] = '(';
146 octave_idx_type i;
147
148 for (i = 1; i < dim; i++)
149 {
150 pre[2*i-1] = '_';
151 pre[2*i] = ',';
152 }
153
154 pre[2*i-1] = '\0'; // i == min (1, dim)
155 }
156 else
157 {
158 sprintf (pre, "(...[x%d]...", dim-1);
159 }
160
161 if (static_cast <unsigned int> (nd-dim) < 5)
162 {
163 for (octave_idx_type i = 0; i < nd-dim; i++)
164 {
165 post[2*i] = ',';
166 post[2*i+1] = '_';
167 }
168
169 if (nd >= dim)
170 {
171 post[2*(nd-dim)] = ')';
172 post[2*(nd-dim)+1] = '\0';
173 }
174 }
175 else
176 sprintf (post, "...[x%d]...)", nd-dim);
177 }
178
179 const char *v;
180
181 if (var[0] == '\0' || var == "<unknown>")
182 v = "index ";
183 else
184 v = var.c_str ();
185
186 snprintf (output, buf_len, "%s%s%s%s", v, pre, idx(), post);
187
188 return output;
189 }
190
191 class invalid_index : public index_exception
192 {
193 public:
194
195 invalid_index (const char *value, octave_idx_type ndim,
196 octave_idx_type dimen)
197 : index_exception (value, ndim, dimen)
198 { }
199
200 const char* explain (void) const
201 {
202 #ifdef USE_64_BIT_IDX_T
203 return "subscripts must be either integers 1 to (2^63)-1 or logicals";
204 #else
205 return "subscripts must be either integers 1 to (2^31)-1 or logicals";
206 #endif
207 }
208
209 // ID of error to throw
210 const char* id (void) const
211 {
212 return error_id_invalid_index;
213 }
214 };
215
216 // Complain of an index that is: negative, fractional, or too big.
217
218 void
219 gripe_invalid_index (const char *idx, octave_idx_type nd,
220 octave_idx_type dim, const char * /* var */)
221 {
222 invalid_index e (idx, nd, dim);
223
224 throw e;
225 }
226
227 void
228 gripe_invalid_index (octave_idx_type n, octave_idx_type nd,
229 octave_idx_type dim, const char *var)
230 {
231 // FIXME: don't use a fixed size buffer!
232 char buf [100];
233
234 sprintf (buf, "%d", n+1);
235
236 gripe_invalid_index (buf, nd, dim, var);
237 }
238
239 void
240 gripe_invalid_index (double n, octave_idx_type nd, octave_idx_type dim,
241 const char *var)
242 {
243 // FIXME: don't use a fixed size buffer!
244 char buf [100];
245
246 sprintf (buf, "%g", n+1);
247
248 gripe_invalid_index (buf, nd, dim, var);
249 }
250
251
252 // Gripe and exception for read access beyond the bounds of an array.
253
254 class out_of_range : public index_exception
255 {
256 public:
257
258 out_of_range (const char *value, octave_idx_type nd_in,octave_idx_type dim_in)
259 : index_exception (value, nd_in, dim_in), extent(0)
260 { }
261
262 const char* explain (void) const
263 {
264 static std::string expl; // should probably be member variable, but
265 // then explain() can't be const.
266
267 if (nd >= size.length ()) // if not an index slice
268 {
269 if (var != "")
270 expl = "but " + var + " has size ";
271 else
272 expl = "but object has size ";
273
274 expl = expl + size.str ('x');
275 }
276 else
277 {
278 // FIXME: don't use a fixed size buffer!
279 char buf [100];
280 sprintf (buf, "%d", extent);
281 expl = "out of bound " + std::string (buf);
282 }
283
284 return expl.c_str ();
285 }
286
287 // ID of error to throw.
288 const char* id (void) const
289 {
290 return (error_id_index_out_of_bounds);
291 }
292
293 void set_size (const dim_vector& size_in) { size = size_in; }
294
295 void set_extent (octave_idx_type ext) { extent = ext; }
296
297 private:
298
299 dim_vector size; // dimension of object being accessed
300
301 octave_idx_type extent; // length of dimension being accessed
302 };
303
304 // Complain of an index that is out of range, but we don't know matrix size
305 void
93 gripe_index_out_of_range (int nd, int dim, octave_idx_type idx, 306 gripe_index_out_of_range (int nd, int dim, octave_idx_type idx,
94 octave_idx_type ext) 307 octave_idx_type ext)
95 { 308 {
96 const char *err_id = error_id_index_out_of_bounds; 309 char buf [100];
97 310 sprintf (buf, "%d", idx);
98 switch (nd) 311 out_of_range e (buf, nd, dim);
99 { 312
100 case 1: 313 e.set_extent (ext);
101 (*current_liboctave_error_with_id_handler) 314 dim_vector d (1,1,1,1,1,1,1); // make explain() give extent not size
102 (err_id, "A(I): index out of bounds; value %d out of bound %d", 315 e.set_size (d);
103 idx, ext); 316 throw e;
104 break; 317 }
105 318
106 case 2: 319 // Complain of an index that is out of range
107 (*current_liboctave_error_with_id_handler) 320 void
108 (err_id, "A(I,J): %s index out of bounds; value %d out of bound %d", 321 gripe_index_out_of_range (int nd, int dim, octave_idx_type idx,
109 (dim == 1) ? "row" : "column", idx, ext); 322 octave_idx_type ext, const dim_vector& d)
110 break; 323 {
111 324 char buf [100];
112 default: 325 sprintf (buf, "%d", idx);
113 (*current_liboctave_error_with_id_handler) 326 out_of_range e (buf, nd, dim);
114 (err_id, "A(I,J,...): index to dimension %d out of bounds; value %d out of bound %d", 327
115 dim, idx, ext); 328 e.set_extent (ext);
116 break; 329 e.set_size (d);
117 } 330 throw e;
118 } 331 }
119
120 void
121 gripe_del_index_out_of_range (bool is1d, octave_idx_type idx,
122 octave_idx_type ext)
123 {
124 const char *err_id = error_id_index_out_of_bounds;
125
126 (*current_liboctave_error_with_id_handler)
127 (err_id, "A(%s) = []: index out of bounds; value %d out of bound %d",
128 is1d ? "I" : "..,I,..", idx, ext);
129 }
130
131 void
132 gripe_invalid_index (void)
133 {
134 const char *err_id = error_id_invalid_index;
135
136 (*current_liboctave_error_with_id_handler)
137 #ifdef USE_64_BIT_IDX_T
138 (err_id, "subscript indices must be either positive integers less than 2^63 or logicals");
139 #else
140 (err_id, "subscript indices must be either positive integers less than 2^31 or logicals");
141 #endif
142 }
143
144 // FIXME: the following is a common error message to resize,
145 // regardless of whether it's called from assign or elsewhere. It
146 // seems OK to me, but eventually the gripe can be specialized.
147 // Anyway, propagating various error messages into procedure is, IMHO,
148 // a nonsense. If anything, we should change error handling here (and
149 // throughout liboctave) to allow custom handling of errors
150 332
151 void 333 void
152 gripe_invalid_resize (void) 334 gripe_invalid_resize (void)
153 { 335 {
154 (*current_liboctave_error_with_id_handler) 336 (*current_liboctave_error_with_id_handler)
155 ("Octave:invalid-resize", 337 ("Octave:invalid-resize",
156 "Invalid resizing operation or ambiguous assignment to an out-of-bounds array element"); 338 "Invalid resizing operation or ambiguous assignment to an out-of-bounds array element");
157 }
158
159 void
160 gripe_invalid_assignment_size (void)
161 {
162 (*current_liboctave_error_handler)
163 ("A(I) = X: X must have the same size as I");
164 }
165
166 void
167 gripe_assignment_dimension_mismatch (void)
168 {
169 (*current_liboctave_error_handler)
170 ("A(I,J,...) = X: dimensions mismatch");
171 } 339 }
172 340
173 void 341 void
174 gripe_singular_matrix (double rcond) 342 gripe_singular_matrix (double rcond)
175 { 343 {
184 (*current_liboctave_warning_with_id_handler) 352 (*current_liboctave_warning_with_id_handler)
185 (warning_id_nearly_singular_matrix, 353 (warning_id_nearly_singular_matrix,
186 "matrix singular to machine precision, rcond = %g", rcond); 354 "matrix singular to machine precision, rcond = %g", rcond);
187 } 355 }
188 } 356 }
357
358 /* Tests in test/index.tst */