From 968eeb4b2b719ff628241e2ea5a6e89bbb677fcf Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Fri, 4 Dec 2020 03:19:47 +0100 Subject: [PATCH] idx: Clarify that idx_t always behaves like a signed type. Suggested by Paul Eggert in . * lib/idx.h: Clarify that idx_t always behaves like a signed type. Don't test UNSIGNED_IDX_T. --- ChangeLog | 8 ++++++++ lib/idx.h | 60 ++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4b6e880744..711294c32f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2020-12-03 Bruno Haible + + idx: Clarify that idx_t always behaves like a signed type. + Suggested by Paul Eggert in + . + * lib/idx.h: Clarify that idx_t always behaves like a signed type. + Don't test UNSIGNED_IDX_T. + 2020-12-03 Bruno Haible idx: New module. diff --git a/lib/idx.h b/lib/idx.h index 6574b1bcb5..0095467dcd 100644 --- a/lib/idx.h +++ b/lib/idx.h @@ -25,16 +25,35 @@ #include /* 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 @@ -67,27 +86,22 @@ 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. - - 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 + , + 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 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 */ -- 2.39.5