]> Savannah Git Hosting - gnulib.git/commitdiff
idx: Clarify that idx_t always behaves like a signed type.
authorBruno Haible <bruno@clisp.org>
Fri, 4 Dec 2020 02:19:47 +0000 (03:19 +0100)
committerBruno Haible <bruno@clisp.org>
Fri, 4 Dec 2020 02:27:20 +0000 (03:27 +0100)
Suggested by Paul Eggert in
<https://lists.gnu.org/archive/html/bug-gnulib/2020-12/msg00034.html>.

* lib/idx.h: Clarify that idx_t always behaves like a signed type.
Don't test UNSIGNED_IDX_T.

ChangeLog
lib/idx.h

index 4b6e88074472ab196e36c97492950f04ee37dbd2..711294c32f8a7b0aea579092bcb145c3916f35f9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2020-12-03  Bruno Haible  <bruno@clisp.org>
+
+       idx: Clarify that idx_t always behaves like a signed type.
+       Suggested by Paul Eggert in
+       <https://lists.gnu.org/archive/html/bug-gnulib/2020-12/msg00034.html>.
+       * lib/idx.h: Clarify that idx_t always behaves like a signed type.
+       Don't test UNSIGNED_IDX_T.
+
 2020-12-03  Bruno Haible  <bruno@clisp.org>
 
        idx: New module.
index 6574b1bcb54754a96a7e2a47195217cc992df4e9..0095467dcd0a8d37c39e689f420e89999acbd58e 100644 (file)
--- a/lib/idx.h
+++ b/lib/idx.h
 #include <stdint.h>
 
 /* The type 'idx_t' holds an (array) index or an (object) size.
-   Its implementation is a signed or unsigned integer type, capable of holding
-   the values
+   Its implementation is a signed integer type, capable of holding the values
      0..2^63-1 (on 64-bit platforms) or
      0..2^31-1 (on 32-bit platforms).
 
-   Why not use 'size_t' directly?
+   Why a signed integer type?
 
      * Security: Signed types can be checked for overflow via
        '-fsanitize=undefined', but unsigned types cannot.
 
+     * Comparisons without surprises: ISO C99 § 6.3.1.8 specifies a few
+       surprising results for comparisons, such as
+
+           (int) -3 < (unsigned long) 7  =>  false
+           (int) -3 < (unsigned int) 7   =>  false
+       and on 32-bit machines:
+           (long) -3 < (unsigned int) 7  =>  false
+
+       This is surprising because the natural comparison order is by
+       value in the realm of infinite-precision signed integers (ℤ).
+
+       The best way to get rid of such surprises is to use signed types
+       for numerical integer values, and use unsigned types only for
+       bit masks and enums.
+
+   Why not use 'size_t' directly?
+
+     * Because 'size_t' is an unsigned type, and a signed type is better.
+       See above.
+
    Why not use 'ptrdiff_t' directly?
 
      * Maintainability: When reading and modifying code, it helps to know that
        constrained to a certain range of values) may be added to C compilers
        or to the C standard.  Several programming languages (Ada, Haskell,
        Common Lisp, Pascal) already have range types.  Such range types may
-       help producing good code and good warnings.  The type 'idx_t'
-       could then be typedef'ed to a range type.  */
-
-/* The user can define UNSIGNED_IDX_T, to get a different set of compiler
-   warnings.  */
-#if defined UNSIGNED_IDX_T
-# if __clang_version__ >= 11 && 0
-/* It would be tempting to use the clang "extended integer types", which are a
-   special case of range types.
-   <https://clang.llvm.org/docs/LanguageExtensions.html#extended-integer-types>
-   However, these types don't support binary operators with plain integer
-   types (e.g. expressions such as x > 1).  */
-typedef unsigned _ExtInt (PTRDIFF_WIDTH - 1) idx_t;
-# else
-/* Use an unsigned type as wide as 'ptrdiff_t'.
-   ISO C does not mandate that 'size_t' and 'ptrdiff_t' have the same size, but
-   it is so an all platforms we have seen since 1990.  */
-typedef size_t idx_t;
-# endif
+       help producing good code and good warnings.  The type 'idx_t' could
+       then be typedef'ed to a (signed!) range type.  */
+
+#if 0
+/* In the future, idx_t could be typedef'ed to a signed range type.  */
+/* Note: The clang "extended integer types", supported in Clang 11 or newer
+   <https://clang.llvm.org/docs/LanguageExtensions.html#extended-integer-types>,
+   are a special case of range types.  However, these types don't support binary
+   operators with plain integer types (e.g. expressions such as x > 1).
+   Therefore, they don't behave like signed types (and not like unsigned types
+   either).  So, we cannot use them here.  */
+typedef <some_range_type> idx_t;
 #else
 /* Use the signed type 'ptrdiff_t' by default.  */
+/* Note: ISO C does not mandate that 'size_t' and 'ptrdiff_t' have the same
+   size, but it is so an all platforms we have seen since 1990.  */
 typedef ptrdiff_t idx_t;
 #endif
 
@@ -98,7 +112,7 @@ typedef ptrdiff_t idx_t;
 # define IDX_MAX PTRDIFF_MAX
 #endif
 
-/* IDX_WIDTH is the number of bits in an idx_t.  */
+/* IDX_WIDTH is the number of bits in an idx_t (31 or 63).  */
 #define IDX_WIDTH (PTRDIFF_WIDTH - 1)
 
 #endif /* _IDX_H */