changeset 30836:97504d2edcc7

maint: Merge stable to default.
author John W. Eaton <jwe@octave.org>
date Thu, 17 Mar 2022 02:52:53 -0400
parents d87406970676 (current diff) 2989202f92f8 (diff)
children ecde6a40fb72
files libinterp/octave-value/ov-base.cc libinterp/octave-value/ov-base.h libinterp/octave-value/ov-range.cc libinterp/octave-value/ov.cc libinterp/octave-value/ov.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-exp.h
diffstat 13 files changed, 574 insertions(+), 150 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/octave-value/ov-base.cc	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/octave-value/ov-base.cc	Thu Mar 17 02:52:53 2022 -0400
@@ -810,18 +810,22 @@
   err_wrong_type_arg ("octave_base_value::cellstr_value()", type_name ());
 }
 
+octave::range<double>
+octave_base_value::range_value (void) const
+{
+  err_wrong_type_arg ("octave_base_value::range_value()", type_name ());
+}
+
+// For now, disable all but range<double>.
+
+#if 0
+
 octave::range<float>
 octave_base_value::float_range_value (void) const
 {
   err_wrong_type_arg ("octave_base_value::float_range_value()", type_name ());
 }
 
-octave::range<double>
-octave_base_value::range_value (void) const
-{
-  err_wrong_type_arg ("octave_base_value::range_value()", type_name ());
-}
-
 octave::range<octave_int8>
 octave_base_value::int8_range_value (void) const
 {
@@ -870,6 +874,8 @@
   err_wrong_type_arg ("octave_base_value::uint64_range_value()", type_name ());
 }
 
+#endif
+
 octave_map
 octave_base_value::map_value (void) const
 {
--- a/libinterp/octave-value/ov-base.h	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/octave-value/ov-base.h	Thu Mar 17 02:52:53 2022 -0400
@@ -630,9 +630,13 @@
 
   virtual Array<std::string> cellstr_value (void) const;
 
-  virtual octave::range<float> float_range_value (void) const;
+  virtual octave::range<double> range_value (void) const;
+
+  // For now, disable all but range<double>.
 
-  virtual octave::range<double> range_value (void) const;
+#if 0
+
+  virtual octave::range<float> float_range_value (void) const;
 
   virtual octave::range<octave_int8> int8_range_value (void) const;
 
@@ -650,6 +654,8 @@
 
   virtual octave::range<octave_uint64> uint64_range_value (void) const;
 
+#endif
+
   virtual octave_map map_value (void) const;
 
   virtual octave_scalar_map scalar_map_value (void) const;
--- a/libinterp/octave-value/ov-range.cc	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/octave-value/ov-range.cc	Thu Mar 17 02:52:53 2022 -0400
@@ -67,10 +67,14 @@
 #if defined (HAVE_HDF5)
 
 template <>
-octave_hdf5_id ov_range<float>::hdf5_save_type = H5T_NATIVE_FLOAT;
+octave_hdf5_id ov_range<double>::hdf5_save_type = H5T_NATIVE_DOUBLE;
+
+// For now, disable all but ov_range<double>.
+
+#  if 0
 
 template <>
-octave_hdf5_id ov_range<double>::hdf5_save_type = H5T_NATIVE_DOUBLE;
+octave_hdf5_id ov_range<float>::hdf5_save_type = H5T_NATIVE_FLOAT;
 
 template <>
 octave_hdf5_id ov_range<octave_int8>::hdf5_save_type = H5T_NATIVE_INT8;
@@ -96,15 +100,21 @@
 template <>
 octave_hdf5_id ov_range<octave_uint64>::hdf5_save_type = H5T_NATIVE_UINT64;
 
+#  endif
+
 #else
 
 template <>
+octave_hdf5_id ov_range<double>::hdf5_save_type = 0;
+
+// For now, disable all but ov_range<double>.
+
+#if 0
+
+template <>
 octave_hdf5_id ov_range<float>::hdf5_save_type = 0;
 
 template <>
-octave_hdf5_id ov_range<double>::hdf5_save_type = 0;
-
-template <>
 octave_hdf5_id ov_range<octave_int8>::hdf5_save_type = 0;
 
 template <>
@@ -128,14 +138,20 @@
 template <>
 octave_hdf5_id ov_range<octave_uint64>::hdf5_save_type = 0;
 
+#  endif
+
 #endif
 
+DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<double>,
+                                              "range", "double");
+
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<float>,
                                               "float_range", "single");
 
-DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<double>,
-                                              "range", "double");
-
 DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_int8>,
                                               "int8_range", "int8");
 
@@ -160,6 +176,8 @@
 DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<octave_uint64>,
                                               "uint64_range", "uint64");
 
+#endif
+
 template <typename T>
 static octave_base_value *
 default_numeric_conversion_function (const octave_base_value& a)
@@ -395,6 +413,17 @@
 }
 
 template <typename T>
+octave::range<double>
+ov_range<T>::range_value (void) const
+{
+  err_wrong_type_arg ("ov_range<T>::range_value()", type_name ());
+}
+
+// For now, disable all but ov_range<double>.
+
+#if 0
+
+template <typename T>
 octave::range<float>
 ov_range<T>::float_range_value (void) const
 {
@@ -402,13 +431,6 @@
 }
 
 template <typename T>
-octave::range<double>
-ov_range<T>::range_value (void) const
-{
-  err_wrong_type_arg ("ov_range<T>::range_value()", type_name ());
-}
-
-template <typename T>
 octave::range<octave_int8>
 ov_range<T>::int8_range_value (void) const
 {
@@ -464,6 +486,8 @@
   err_wrong_type_arg ("ov_range<T>::uint64_range_value ()", type_name ());
 }
 
+#endif
+
 template <typename T>
 octave_value
 ov_range<T>::convert_to_str_internal (bool pad, bool force, char type) const
@@ -691,6 +715,10 @@
 
 // specialize for saving with "reverse" flag
 
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 template <>
 bool
 ov_range<octave_uint8>::save_ascii (std::ostream& os)
@@ -719,6 +747,8 @@
   return xsave_ascii (os, m_range, true);
 }
 
+#endif
+
 template <typename T>
 bool
 xload_ascii (std::istream& is, octave::range<T>& r, const bool with_reverse)
@@ -756,6 +786,10 @@
 
 // specialize for loading with "reverse" flag
 
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 template <>
 bool
 ov_range<octave_uint8>::load_ascii (std::istream& is)
@@ -784,6 +818,8 @@
   return xload_ascii (is, m_range, true);
 }
 
+#endif
+
 /*
 %!test
 %! a = b = 1:4;
@@ -844,6 +880,10 @@
   return xsave_binary (os, save_as_floats, m_range, false);
 }
 
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 template <>
 bool
 ov_range<octave_uint8>::save_binary (std::ostream& os, bool save_as_floats)
@@ -872,6 +912,8 @@
   return xsave_binary (os, save_as_floats, m_range, true);
 }
 
+#endif
+
 template <typename T>
 bool
 xload_binary (std::istream& is, bool swap,
@@ -923,6 +965,10 @@
   return xload_binary (is, swap, fmt, m_range, false);
 }
 
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 template <>
 bool
 ov_range<octave_uint8>::load_binary (std::istream& is, bool swap,
@@ -955,6 +1001,8 @@
   return xload_binary (is, swap, fmt, m_range, true);
 }
 
+#endif
+
 /*
 %!test
 %! a = b = 1:4;
@@ -1102,6 +1150,10 @@
 #endif
 }
 
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 template <>
 bool
 ov_range<octave_uint8>::save_hdf5 (octave_hdf5_id loc_id, const char *name,
@@ -1178,6 +1230,8 @@
 #endif
 }
 
+#endif
+
 #if defined (HAVE_HDF5)
 
 template <typename T>
@@ -1261,6 +1315,10 @@
 #endif
 }
 
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 template <>
 bool
 ov_range<octave_uint8>::load_hdf5 (octave_hdf5_id loc_id, const char *name)
@@ -1325,6 +1383,8 @@
 #endif
 }
 
+#endif
+
 /*
 %!testif HAVE_HDF5
 %! a = b = 1:4;
@@ -1384,15 +1444,19 @@
 // Specializations.
 
 template <>
-octave::range<float>
-ov_range<float>::float_range_value (void) const
+octave::range<double>
+ov_range<double>::range_value (void) const
 {
   return m_range;
 }
 
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 template <>
-octave::range<double>
-ov_range<double>::range_value (void) const
+octave::range<float>
+ov_range<float>::float_range_value (void) const
 {
   return m_range;
 }
@@ -1453,6 +1517,8 @@
   return m_range;
 }
 
+#endif
+
 template <>
 octave::idx_vector
 ov_range<double>::index_vector (bool require_integers) const
--- a/libinterp/octave-value/ov-range.h	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/octave-value/ov-range.h	Thu Mar 17 02:52:53 2022 -0400
@@ -32,6 +32,7 @@
 
 #include <iosfwd>
 #include <string>
+#include <type_traits>
 
 #include "Array-fwd.h"
 #include "Range.h"
@@ -50,7 +51,8 @@
 
 class octave_value_list;
 
-// Range values.
+// For now, we only need ov_range<double> but we don't attempt to
+// enforce that restriction.
 
 template <typename T>
 class
@@ -384,9 +386,13 @@
     return raw_array_value ();
   }
 
-  OCTINTERP_API octave::range<float> float_range_value (void) const;
+  OCTINTERP_API octave::range<double> range_value (void) const;
+
+// For now, disable all but ov_range<double>.
 
-  OCTINTERP_API octave::range<double> range_value (void) const;
+#if 0
+
+  OCTINTERP_API octave::range<float> float_range_value (void) const;
 
   OCTINTERP_API octave::range<octave_int8> int8_range_value (void) const;
 
@@ -404,6 +410,8 @@
 
   OCTINTERP_API octave::range<octave_uint64> uint64_range_value (void) const;
 
+#endif
+
   OCTINTERP_API octave_value
   convert_to_str_internal (bool pad, bool force, char type) const;
 
@@ -494,8 +502,13 @@
   DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
 };
 
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, double)
+
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, float)
-DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, double)
 DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_int8)
 DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_int16)
 DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_int32)
@@ -505,17 +518,23 @@
 DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_uint32)
 DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, octave_uint64)
 
+#endif
+
 // Specializations.
 
 template <>
+OCTINTERP_API octave::range<double>
+ov_range<double>::range_value (void) const;
+
+// For now, disable all but ov_range<double>.
+
+#if 0
+
+template <>
 OCTINTERP_API octave::range<float>
 ov_range<float>::float_range_value (void) const;
 
 template <>
-OCTINTERP_API octave::range<double>
-ov_range<double>::range_value (void) const;
-
-template <>
 OCTINTERP_API octave::range<octave_int8>
 ov_range<octave_int8>::int8_range_value (void) const;
 
@@ -547,6 +566,8 @@
 OCTINTERP_API octave::range<octave_uint64>
 ov_range<octave_uint64>::uint64_range_value (void) const;
 
+#endif
+
 // The following specializations are here to preserve previous Range
 // performance until solutions can be generalized for other types.
 
@@ -576,8 +597,13 @@
 ov_range<double>::print_raw (std::ostream& os, bool pr_as_read_syntax) const;
 
 
+typedef ov_range<double> octave_double_range;
+
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 typedef ov_range<float> octave_float_range;
-typedef ov_range<double> octave_double_range;
 
 typedef ov_range<octave_int8> octave_int8_range;
 typedef ov_range<octave_int16> octave_int16_range;
@@ -589,6 +615,8 @@
 typedef ov_range<octave_uint32> octave_uint32_range;
 typedef ov_range<octave_uint64> octave_uint64_range;
 
+#endif
+
 typedef octave_double_range octave_range;
 
 #endif
--- a/libinterp/octave-value/ov.cc	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/octave-value/ov.cc	Thu Mar 17 02:52:53 2022 -0400
@@ -29,6 +29,8 @@
 
 #include <cmath>
 
+#include <type_traits>
+
 #include "data-conv.h"
 #include "quit.h"
 #include "str-vec.h"
@@ -1091,23 +1093,18 @@
     return dynamic_cast<octave_base_value *> (new octave_matrix (r.matrix_value ()));
 }
 
-octave_value::octave_value (const octave::range<char>& r, char type,
-                            bool /*force_range*/)
-#if 0
-  : m_rep (force_range || optimize_range
-           ? dynamic_cast<octave_base_value *> (new octave_char_range (r, type))
-           : dynamic_cast<octave_base_value *> (type == '"'
-                                                ? new octave_char_matrix_dq_str (r.array_value ())
-                                                : new octave_char_matrix_sq_str (r.array_value ())))
-#else
-  : m_rep (type == '"'
-           ? new octave_char_matrix_dq_str (r.array_value ())
-           : new octave_char_matrix_sq_str (r.array_value ()))
-#endif
+octave_value::octave_value (const octave::range<double>& r, bool force_range)
+  : m_rep (force_range || Voptimize_range
+           ? dynamic_cast<octave_base_value *> (new ov_range<double> (r))
+           : dynamic_cast<octave_base_value *> (new octave_matrix (r.array_value ())))
 {
   maybe_mutate ();
 }
 
+// For now, disable all but range<double>.
+
+#if 0
+
 octave_value::octave_value (const octave::range<float>& r, bool force_range)
   : m_rep (force_range || Voptimize_range
            ? dynamic_cast<octave_base_value *> (new ov_range<float> (r))
@@ -1116,14 +1113,6 @@
   maybe_mutate ();
 }
 
-octave_value::octave_value (const octave::range<double>& r, bool force_range)
-  : m_rep (force_range || Voptimize_range
-           ? dynamic_cast<octave_base_value *> (new ov_range<double> (r))
-           : dynamic_cast<octave_base_value *> (new octave_matrix (r.array_value ())))
-{
-  maybe_mutate ();
-}
-
 octave_value::octave_value (const octave::range<octave_int8>& r,
                             bool force_range)
   : m_rep (force_range || Voptimize_range
@@ -1196,6 +1185,25 @@
   maybe_mutate ();
 }
 
+octave_value::octave_value (const octave::range<char>& r, char type,
+                            bool /*force_range*/)
+#if 0
+  : m_rep (force_range || optimize_range
+           ? dynamic_cast<octave_base_value *> (new octave_char_range (r, type))
+           : dynamic_cast<octave_base_value *> (type == '"'
+                                                ? new octave_char_matrix_dq_str (r.array_value ())
+                                                : new octave_char_matrix_sq_str (r.array_value ())))
+#else
+  : m_rep (type == '"'
+           ? new octave_char_matrix_dq_str (r.array_value ())
+           : new octave_char_matrix_sq_str (r.array_value ()))
+#endif
+{
+  maybe_mutate ();
+}
+
+#endif
+
 octave_value::octave_value (const octave_map& m)
   : m_rep (new octave_struct (m))
 {
@@ -2227,8 +2235,13 @@
 XVALUE_EXTRACTOR (Cell, xcell_value, cell_value)
 XVALUE_EXTRACTOR (Array<std::string>, xcellstr_value, cellstr_value)
 
+XVALUE_EXTRACTOR (octave::range<double>, xrange_value, range_value)
+
+// For now, disable all but ov_range<double>.
+
+#if 0
+
 XVALUE_EXTRACTOR (octave::range<float>, xfloat_range_value, float_range_value)
-XVALUE_EXTRACTOR (octave::range<double>, xrange_value, range_value)
 XVALUE_EXTRACTOR (octave::range<octave_int8>, xint8_range_value, int8_range_value)
 XVALUE_EXTRACTOR (octave::range<octave_int16>, xint16_range_value, int16_range_value)
 XVALUE_EXTRACTOR (octave::range<octave_int32>, xint32_range_value, int32_range_value)
@@ -2238,6 +2251,8 @@
 XVALUE_EXTRACTOR (octave::range<octave_uint32>, xuint32_range_value, uint32_range_value)
 XVALUE_EXTRACTOR (octave::range<octave_uint64>, xuint64_range_value, uint64_range_value)
 
+#endif
+
 XVALUE_EXTRACTOR (octave_map, xmap_value, map_value)
 XVALUE_EXTRACTOR (octave_scalar_map, xscalar_map_value, scalar_map_value)
 
@@ -3022,76 +3037,302 @@
       error ("colon operator %s invalid (not an integer or out of range for given integer type)", op_str);
   }
 
-// Templated version used for various integer types (int8, uint16, ...)
-  template <typename T>
+  // Return the difference between two unsigned integers as an unsigned
+  // integer of the same type.
+
+  template <typename UT,
+            typename std::enable_if<(std::is_integral<UT>::value
+                                     && std::is_unsigned<UT>::value),
+                                    bool>::type = true>
+  UT
+  integer_difference (UT a, UT b)
+  {
+    return a > b ? a - b : b - a;
+  }
+
+  // Return the difference between two signed integers as an unsigned
+  // integer corresponding to the signed type.
+
+  template <typename ST,
+            typename UT = typename std::make_unsigned<ST>::type,
+            typename std::enable_if<(std::is_integral<ST>::value
+                                     && std::is_signed<ST>::value),
+                                    bool>::type = true>
+  UT
+  integer_difference (ST a, ST b)
+  {
+    // Map to unsigned.
+    // Idea from https://stackoverflow.com/questions/10589559
+
+    static const UT offset
+      = UT (0) - static_cast<UT> (std::numeric_limits<ST>::min ());
+
+    UT au = static_cast<UT> (a) + offset;
+    UT bu = static_cast<UT> (b) + offset;
+
+    return integer_difference (au, bu);
+  }
+
+  // Number of elements in an integer range taking care to avoid
+  // overflow.  Base and limit are of the same type.  If they are
+  // unsigned, then increment is also of the same type.  If they are
+  // signed, then the type of increment is the unsigned type
+  // corresponding to T.  Assumes that the base and limit values are
+  // consistent with the sign of the original increment (not an empty
+  // range) so we can calculate numel with the absolute value of the
+  // increment and the absolute difference between the base and limit
+  // values.
+
+  template <typename T,
+            typename UT = typename std::make_unsigned<T>::type,
+            typename std::enable_if<std::is_integral<T>::value,
+                                    bool>::type = true>
+  octave_idx_type
+  range_numel_aux (T base, UT unsigned_increment, T limit)
+  {
+    // Adding one to DIFF/INCREMENT may overflow, so check whether it is
+    // out of range before adding.
+
+    UT nel_m1 = integer_difference (limit, base) / unsigned_increment;
+
+    // FIXME: fix error message.
+    if (nel_m1 > std::numeric_limits<octave_idx_type>::max () - 1)
+      error ("too many elements for range!");
+
+    return static_cast<octave_idx_type> (nel_m1) + 1;
+  }
+
+  // Number of elements in an integer range base:increment:limit.  Base,
+  // increment, and limit are of the same signed type.
+
+  template <typename ST,
+            typename std::enable_if<(std::is_integral<ST>::value
+                                     && std::is_signed<ST>::value),
+                                    bool>::type = true>
+  octave_idx_type
+  range_numel (ST base, ST increment, ST limit)
+  {
+    typedef typename std::make_unsigned<ST>::type UT;
+
+    if (increment == 0
+        || (increment > 0 && base > limit)
+        || (increment < 0 && base < limit))
+      return 0;
+
+    UT unsigned_increment = (increment < 0
+                             ? UT (0) - static_cast<UT> (increment)
+                             : static_cast<UT> (increment));
+
+    return range_numel_aux (base, unsigned_increment, limit);
+  }
+
+  // Number of elements in an integer range base:increment:limit.  Base,
+  // increment, and limit are unsigned and of the same type.
+
+  template <typename UT,
+            typename std::enable_if<(std::is_integral<UT>::value
+                                     && std::is_unsigned<UT>::value),
+                                    bool>::type = true>
+  octave_idx_type
+  range_numel (UT base, UT increment, UT limit)
+  {
+    // Unsigned, INCREMENT is always >= 0.
+    if (increment == 0 || base > limit)
+      return 0;
+
+    return range_numel_aux (base, increment, limit);
+  }
+
+  // Number of elements in an integer range base:increment:limit.  Base
+  // and limit are of the same type and increment is a double value.
+
+  template <typename T,
+            typename UT = typename std::make_unsigned<T>::type,
+            typename std::enable_if<std::is_integral<T>::value,
+                                    bool>::type = true>
+  octave_idx_type
+  range_numel (T base, double increment, T limit)
+  {
+    double intpart;
+    if (math::isnan (increment) || std::modf (increment, &intpart) != 0.0)
+      error ("colon operator increment invalid (not an integer)");
+
+    if (increment == 0
+        || (increment > 0 && base > limit)
+        || (increment < 0 && base < limit))
+      return 0;
+
+    static const UT max_val = std::numeric_limits<UT>::max ();
+
+    double abs_increment = std::abs (increment);
+
+    if (abs_increment > max_val)
+      return 1;
+
+    return range_numel_aux (base, static_cast<UT> (abs_increment), limit);
+  }
+
+  // Make a range from integer values.  Increment may be integer or double.
+
+  template <typename T,
+            typename IT,
+            typename std::enable_if<(std::is_integral<T>::value
+                                     && std::is_arithmetic<IT>::value),
+                                    bool>::type = true>
   octave_value
-  make_range (const octave_value& base, const octave_value& increment,
-              const octave_value& limit, bool for_cmd_expr)
+  make_int_range (T base, IT increment, T limit)
+  {
+    octave_idx_type nel = range_numel (base, increment, limit);
+
+    // For now, we create arrays instead of range<T> for all types
+    // except double.
+
+    Array<octave_int<T>> result (dim_vector (1, nel));
+
+    if (nel > 0)
+      {
+        T val = base;
+        result.xelem (0) = val;
+        for (octave_idx_type i = 1; i < nel; i++)
+          {
+            val += increment;
+            result.xelem (i) = val;
+          }
+      }
+
+    return octave_value (result);
+  }
+
+  // Make a range from floating point values.
+
+  // FIXME: Try again to define memory efficient range classes for
+  // integer and floating point values?  Maybe with the templates
+  // defined in this file we could do that in a reasonable way?
+  // Regardless of that, it might be good to provide special treatment
+  // of colon expressions in FOR loops so that we can eliminate the
+  // "is_for_cmd_expr / force_rage" flag from the parser and the
+  // octave_value constructors for range objects.
+
+  // NOTE: We define this function separately for float and double so
+  // that we can avoid having to instantiate ov_range<float>.  We DO
+  // instantiate range<float> but only so we can take advantage of the
+  // range<T> class to generate the corresponding array of float values
+  // and not have to duplicate that code here.
+
+  template <typename T,
+            typename std::enable_if<std::is_same<T, double>::value,
+                                    bool>::type = true>
+  octave_value
+  make_float_range (T base, T increment, T limit, bool is_for_cmd_expr)
+  {
+    if (math::isnan (base)
+        || math::isnan (increment)
+        || math::isnan (limit))
+      return octave_value (numeric_limits<T>::NaN ());
+
+    if (increment == 0
+        || (increment > 0 && base > limit)
+        || (increment < 0 && base < limit))
+      return octave_value (Array<T> (dim_vector (1, 0)));
+
+    // At this point, we know that the base and limit values are
+    // consistent with the sign of the increment (not an empty range).
+
+    range<T> r (base, increment, limit);
+
+    if (! is_for_cmd_expr && ! r.is_storable ())
+      error ("range with infinite number of elements cannot be stored");
+
+    return octave_value (r, is_for_cmd_expr);
+  }
+
+  template <typename T,
+            typename std::enable_if<std::is_same<T, float>::value,
+                                    bool>::type = true>
+  octave_value
+  make_float_range (T base, T increment, T limit, bool is_for_cmd_expr)
+  {
+    if (math::isnan (base)
+        || math::isnan (increment)
+        || math::isnan (limit))
+      return octave_value (numeric_limits<T>::NaN ());
+
+    if (increment == 0
+        || (increment > 0 && base > limit)
+        || (increment < 0 && base < limit))
+      return octave_value (Array<T> (dim_vector (1, 0)));
+
+    // At this point, we know that the base and limit values are
+    // consistent with the sign of the increment (not an empty range).
+
+    range<T> r (base, increment, limit);
+
+    if (! is_for_cmd_expr && ! r.is_storable ())
+      error ("range with infinite number of elements cannot be stored");
+
+    return octave_value (r.array_value ());
+  }
+
+  template <typename T,
+            typename std::enable_if<(std::is_same<T, octave_int8>::value
+                                     || std::is_same<T, octave_uint8>::value
+                                     || std::is_same<T, octave_int16>::value
+                                     || std::is_same<T, octave_uint16>::value
+                                     || std::is_same<T, octave_int32>::value
+                                     || std::is_same<T, octave_uint32>::value
+                                     || std::is_same<T, octave_int64>::value
+                                     || std::is_same<T, octave_uint64>::value),
+                                    bool>::type = true>
+  octave_value
+  make_int_range (const octave_value& base, const octave_value& increment,
+                  const octave_value& limit)
   {
     if (base.isempty () || increment.isempty () || limit.isempty ())
-      return octave_value (range<T> (), for_cmd_expr);
-
-    bool reverse = (base.is_uint8_type () || base.is_uint16_type ()
-                    || base.is_uint32_type () || base.is_uint64_type ()
-                    || limit.is_uint8_type () || limit.is_uint16_type ()
-                    || limit.is_uint32_type () || limit.is_uint64_type ())
-                   && increment.scalar_value () < 0;
-
-    octave_value inc = (reverse ? -increment : increment);
+      return octave_value (Array<T> (dim_vector (1, 0)));
 
     check_colon_operand<T> (base, "lower bound");
-    check_colon_operand<T> (inc, "increment");
     check_colon_operand<T> (limit, "upper bound");
 
+    typename T::val_type base_val = octave_value_extract<T> (base).value ();
+    typename T::val_type limit_val = octave_value_extract<T> (limit).value ();
+
+    if (increment.is_double_type ())
+      {
+        double increment_val = increment.double_value ();
+
+        return make_int_range (base_val, increment_val, limit_val);
+      }
+
+    check_colon_operand<T> (increment, "increment");
+
+    typename T::val_type increment_val
+      = octave_value_extract<T> (increment).value ();
+
+    return make_int_range (base_val, increment_val, limit_val);
+  }
+
+  template <typename T,
+            typename std::enable_if<std::is_floating_point<T>::value,
+                                    bool>::type = true>
+  octave_value
+  make_float_range (const octave_value& base, const octave_value& increment,
+                    const octave_value& limit, bool is_for_cmd_expr)
+  {
+    if (base.isempty () || increment.isempty () || limit.isempty ())
+      return octave_value (Array<T> (dim_vector (1, 0)));
+
     T base_val = octave_value_extract<T> (base);
-
-    T increment_val = octave_value_extract<T> (inc);
-
+    T increment_val = octave_value_extract<T> (increment);
     T limit_val = octave_value_extract<T> (limit);
 
-    range<T> r (base_val, increment_val, limit_val, reverse);
-
-    return octave_value (r, for_cmd_expr);
+    return make_float_range (base_val, increment_val, limit_val,
+                             is_for_cmd_expr);
   }
 
-  template <>
+
   octave_value
-  make_range<double> (const octave_value& base, const octave_value& increment,
-                      const octave_value& limit, bool for_cmd_expr)
-  {
-    if (base.isempty () || increment.isempty () || limit.isempty ())
-      return octave_value (range<double> (), for_cmd_expr);
-
-    double base_val = base.double_value ();
-    double increment_val = increment.double_value ();
-    double limit_val = limit.double_value ();
-
-    range<double> r (base_val, increment_val, limit_val);
-
-    return octave_value (r, for_cmd_expr);
-  }
-
-  template <>
-  octave_value
-  make_range<float> (const octave_value& base, const octave_value& increment,
-                     const octave_value& limit, bool for_cmd_expr)
-  {
-    if (base.isempty () || increment.isempty () || limit.isempty ())
-      return octave_value (range<float> (), for_cmd_expr);
-
-    float base_val = base.float_value ();
-    float increment_val = increment.float_value ();
-    float limit_val = limit.float_value ();
-
-    range<float> r (base_val, increment_val, limit_val);
-
-    return octave_value (r, for_cmd_expr);
-  }
-
-  template <>
-  octave_value
-  make_range<char> (const octave_value& base, const octave_value& increment,
-                    const octave_value& limit, bool for_cmd_expr)
+  make_char_range (const octave_value& base, const octave_value& increment,
+                   const octave_value& limit)
   {
     octave_value retval;
 
@@ -3110,7 +3351,7 @@
 
         range<double> tmp (mtx_base(0), mtx_increment(0), mtx_limit(0));
 
-        retval = octave_value (tmp, for_cmd_expr);
+        retval = octave_value (tmp);
       }
 
     return retval.convert_to_str (false, true, type);
@@ -3168,50 +3409,45 @@
     // For compatibility with Matlab, don't allow the range used in
     // a FOR loop expression to be converted to a Matrix.
 
+    // For now, these functions create arrays instead of range<T> for
+    // all types except double.
+
     switch (type_id)
       {
       case btyp_double:
       case btyp_complex:
-        return make_range<double> (base, increment, limit, is_for_cmd_expr);
+        return make_float_range<double> (base, increment, limit, is_for_cmd_expr);
 
       case btyp_float:
       case btyp_float_complex:
-        return make_range<float> (base, increment, limit, is_for_cmd_expr);
+        return make_float_range<float> (base, increment, limit, is_for_cmd_expr);
 
       case btyp_int8:
-        return make_range<octave_int8> (base, increment, limit,
-                                        is_for_cmd_expr);
+        return make_int_range<octave_int8> (base, increment, limit);
 
       case btyp_int16:
-        return make_range<octave_int16> (base, increment, limit,
-                                         is_for_cmd_expr);
+        return make_int_range<octave_int16> (base, increment, limit);
 
       case btyp_int32:
-        return make_range<octave_int32> (base, increment, limit,
-                                         is_for_cmd_expr);
+        return make_int_range<octave_int32> (base, increment, limit);
 
       case btyp_int64:
-        return make_range<octave_int64> (base, increment, limit,
-                                         is_for_cmd_expr);
+        return make_int_range<octave_int64> (base, increment, limit);
 
       case btyp_uint8:
-        return make_range<octave_uint8> (base, increment, limit,
-                                         is_for_cmd_expr);
+        return make_int_range<octave_uint8> (base, increment, limit);
 
       case btyp_uint16:
-        return make_range<octave_uint16> (base, increment, limit,
-                                          is_for_cmd_expr);
+        return make_int_range<octave_uint16> (base, increment, limit);
 
       case btyp_uint32:
-        return make_range<octave_uint32> (base, increment, limit,
-                                          is_for_cmd_expr);
+        return make_int_range<octave_uint32> (base, increment, limit);
 
       case btyp_uint64:
-        return make_range<octave_uint64> (base, increment, limit,
-                                          is_for_cmd_expr);
+        return make_int_range<octave_uint64> (base, increment, limit);
 
       case btyp_char:
-        return make_range<char> (base, increment, limit, is_for_cmd_expr);
+        return make_char_range (base, increment, limit);
 
       case btyp_unknown:
         error ("incompatible types found in range expression");
@@ -3300,8 +3536,13 @@
   octave_diag_matrix::register_type (ti);
   octave_complex_matrix::register_type (ti);
   octave_complex_diag_matrix::register_type (ti);
+  ov_range<double>::register_type (ti);
+
+  // For now, disable all but ov_range<double>.
+
+#if 0
+
   ov_range<float>::register_type (ti);
-  ov_range<double>::register_type (ti);
   ov_range<octave_int8>::register_type (ti);
   ov_range<octave_int16>::register_type (ti);
   ov_range<octave_int32>::register_type (ti);
@@ -3310,6 +3551,9 @@
   ov_range<octave_uint16>::register_type (ti);
   ov_range<octave_uint32>::register_type (ti);
   ov_range<octave_uint64>::register_type (ti);
+
+#endif
+
   octave_bool::register_type (ti);
   octave_bool_matrix::register_type (ti);
   octave_char_matrix_str::register_type (ti);
--- a/libinterp/octave-value/ov.h	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/octave-value/ov.h	Thu Mar 17 02:52:53 2022 -0400
@@ -316,28 +316,44 @@
   }
 #endif
 
-  OCTINTERP_API octave_value (const octave::range<char>& r, char type,
+  OCTINTERP_API octave_value (const octave::range<double>& r,
                               bool force_range = false);
+
+  // For now, disable all but range<double>.
+
+#if 0
+
   OCTINTERP_API octave_value (const octave::range<float>& r,
                               bool force_range = false);
-  OCTINTERP_API octave_value (const octave::range<double>& r,
-                              bool force_range = false);
+
   OCTINTERP_API octave_value (const octave::range<octave_int8>& r,
                               bool force_range = false);
+
   OCTINTERP_API octave_value (const octave::range<octave_int16>& r,
                               bool force_range = false);
+
   OCTINTERP_API octave_value (const octave::range<octave_int32>& r,
                               bool force_range = false);
+
   OCTINTERP_API octave_value (const octave::range<octave_int64>& r,
                               bool force_range = false);
+
   OCTINTERP_API octave_value (const octave::range<octave_uint8>& r,
                               bool force_range = false);
+
   OCTINTERP_API octave_value (const octave::range<octave_uint16>& r,
                               bool force_range = false);
+
   OCTINTERP_API octave_value (const octave::range<octave_uint32>& r,
                               bool force_range = false);
+
   OCTINTERP_API octave_value (const octave::range<octave_uint64>& r,
                               bool force_range = false);
+
+  OCTINTERP_API octave_value (const octave::range<char>& r, char type,
+                              bool force_range = false);
+#endif
+
   OCTINTERP_API octave_value (const octave_map& m);
   OCTINTERP_API octave_value (const octave_scalar_map& m);
   OCTINTERP_API octave_value (const std::map<std::string, octave_value>&);
@@ -1008,12 +1024,16 @@
   Array<std::string> cellstr_value (void) const
   { return m_rep->cellstr_value (); }
 
+  octave::range<double> range_value (void) const
+  { return m_rep->range_value (); }
+
+  // For now, disable all but range<double>.
+
+#if 0
+
   octave::range<float> float_range_value (void) const
   { return m_rep->float_range_value (); }
 
-  octave::range<double> range_value (void) const
-  { return m_rep->range_value (); }
-
   octave::range<octave_int8> int8_range_value (void) const
   { return m_rep->int8_range_value (); }
 
@@ -1038,6 +1058,8 @@
   octave::range<octave_uint64> uint64_range_value (void) const
   { return m_rep->uint64_range_value (); }
 
+#endif
+
   OCTINTERP_API octave_map map_value (void) const;
 
   OCTINTERP_API octave_scalar_map scalar_map_value (void) const;
@@ -1269,12 +1291,16 @@
 
   OCTINTERP_API Array<std::string> xcellstr_value (const char *fmt, ...) const;
 
+  OCTINTERP_API octave::range<double>
+  xrange_value (const char *fmt, ...) const;
+
+  // For now, disable all but range<double>.
+
+#if 0
+
   OCTINTERP_API octave::range<float>
   xfloat_range_value (const char *fmt, ...) const;
 
-  OCTINTERP_API octave::range<double>
-  xrange_value (const char *fmt, ...) const;
-
   OCTINTERP_API octave::range<octave_int8>
   xint8_range_value (const char *fmt, ...) const;
 
@@ -1299,6 +1325,8 @@
   OCTINTERP_API octave::range<octave_uint64>
   xuint64_range_value (const char *fmt, ...) const;
 
+#endif
+
   OCTINTERP_API octave_map xmap_value (const char *fmt, ...) const;
 
   OCTINTERP_API octave_scalar_map
--- a/libinterp/parse-tree/pt-colon.h	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/parse-tree/pt-colon.h	Thu Mar 17 02:52:53 2022 -0400
@@ -89,6 +89,8 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    bool is_colon_expression (void) const { return true; }
+
     octave_value evaluate (tree_evaluator&, int nargout = 1);
 
     octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
--- a/libinterp/parse-tree/pt-eval.cc	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/parse-tree/pt-eval.cc	Thu Mar 17 02:52:53 2022 -0400
@@ -3095,6 +3095,9 @@
             return;
           }
 
+        // For now, disable all but range<double>.
+
+#if 0
         if (rhs.is_int64_type ())
           {
             execute_range_loop (rhs.int64_range_value (), line, ult, loop_body);
@@ -3148,6 +3151,7 @@
             execute_range_loop (rhs.float_range_value (), line, ult, loop_body);
             return;
           }
+#endif
       }
 
     if (rhs.is_scalar_type ())
--- a/libinterp/parse-tree/pt-exp.h	Wed Mar 16 18:45:11 2022 +0100
+++ b/libinterp/parse-tree/pt-exp.h	Thu Mar 17 02:52:53 2022 -0400
@@ -81,6 +81,8 @@
 
     virtual bool is_boolean_expression (void) const { return false; }
 
+    virtual bool is_colon_expression (void) const { return false; }
+
     virtual bool lvalue_ok (void) const { return false; }
 
     virtual bool rvalue_ok (void) const { return false; }
--- a/liboctave/array/Range.cc	Wed Mar 16 18:45:11 2022 +0100
+++ b/liboctave/array/Range.cc	Thu Mar 17 02:52:53 2022 -0400
@@ -328,6 +328,10 @@
     xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
   }
 
+  // For now, only define for float and double.
+
+#if 0
+
   template <>
   void
   range<octave_int8>::init (void)
@@ -384,6 +388,8 @@
     xinit (m_base, m_limit, m_increment, m_reverse, m_final, m_numel);
   }
 
+#endif
+
   template <>
   bool
   range<double>::is_storable (void) const
--- a/liboctave/array/Range.h	Wed Mar 16 18:45:11 2022 +0100
+++ b/liboctave/array/Range.h	Thu Mar 17 02:52:53 2022 -0400
@@ -29,6 +29,7 @@
 #include "octave-config.h"
 
 #include <iosfwd>
+#include <type_traits>
 
 #include "Array-fwd.h"
 #include "dMatrix.h"
@@ -39,8 +40,13 @@
 
 namespace octave
 {
+  // For now, only define for floating point types.  However, we only
+  // need range<float> as a temporary local variable in make_float_range
+  // in ov.cc.
+
   template <typename T>
-  class range
+  class
+  range<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
   {
   public:
 
@@ -123,9 +129,26 @@
       return range<T> (base, increment, final_val, numel, reverse);
     }
 
-    range (const range<T>&) = default;
+    range (const range<T>& r)
+      : m_base (r.m_base), m_increment (r.m_increment),
+        m_limit (r.m_limit), m_final (r.m_final),
+        m_numel (r.m_numel), m_reverse (r.m_reverse)
+    { }
 
-    range<T>& operator = (const range<T>&) = default;
+    range<T>& operator = (const range<T>& r)
+    {
+      if (this != &r)
+        {
+          m_base = r.m_base;
+          m_increment = r.m_increment;
+          m_limit = r.m_limit;
+          m_final = r.m_final;
+          m_numel = r.m_numel;
+          m_reverse = r.m_reverse;
+        }
+
+      return *this;
+    }
 
     ~range (void) = default;
 
@@ -370,6 +393,13 @@
 
   template <> OCTAVE_API void range<double>::init (void);
   template <> OCTAVE_API void range<float>::init (void);
+
+  // For now, only define for floating point types.  However, we only
+  // need range<float> as a temporary local variable in make_float_range
+  // in ov.cc.
+
+#if 0
+
   template <> OCTAVE_API void range<octave_int8>::init (void);
   template <> OCTAVE_API void range<octave_int16>::init (void);
   template <> OCTAVE_API void range<octave_int32>::init (void);
@@ -379,6 +409,8 @@
   template <> OCTAVE_API void range<octave_uint32>::init (void);
   template <> OCTAVE_API void range<octave_uint64>::init (void);
 
+#endif
+
   template <> OCTAVE_API bool range<double>::is_storable (void) const;
   template <> OCTAVE_API bool range<float>::is_storable (void) const;
 
--- a/liboctave/array/range-fwd.h	Wed Mar 16 18:45:11 2022 +0100
+++ b/liboctave/array/range-fwd.h	Thu Mar 17 02:52:53 2022 -0400
@@ -30,7 +30,7 @@
 
 namespace octave
 {
-  template <typename T> class OCTAVE_API range;
+  template <typename T, typename ENABLE = void> class OCTAVE_API range;
 }
 
 #endif
--- a/test/range.tst	Wed Mar 16 18:45:11 2022 +0100
+++ b/test/range.tst	Thu Mar 17 02:52:53 2022 -0400
@@ -491,7 +491,7 @@
 %!error <colon operator lower bound invalid> (1.5:uint8(1):5)
 %!error <colon operator lower bound invalid> (-1:uint8(1):5)
 %!error <colon operator increment invalid> (uint8(1):1.5:5)
-%!error <colon operator increment invalid> (uint8(1):-256:5)
+%!error <colon operator increment invalid> (uint8(1):NaN:5)
 %!error <colon operator upper bound invalid> (uint8(1):1:5.5)
 %!error <colon operator upper bound invalid> (uint8(1):1:256)
 %!error <colon operator upper bound invalid> (uint8(1):-1:-6)