From: Paul Eggert Date: Fri, 7 Feb 2025 22:36:55 +0000 (-0800) Subject: intprops: new macro INT_PROMOTE X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=e8e5e42f0149df984408597db0558820a45e501a;p=gnulib.git intprops: new macro INT_PROMOTE Something like this was requested for Emacs. * doc/intprops.texi (Arithmetic Type Conversion): New section. * lib/intprops.h (INT_PROMOTE): New macro. * tests/test-intprops.c: Test it. --- diff --git a/ChangeLog b/ChangeLog index 610560efa3..a08e7a0220 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2025-02-07 Paul Eggert + + intprops: new macro INT_PROMOTE + Something like this was requested for Emacs. + * doc/intprops.texi (Arithmetic Type Conversion): New section. + * lib/intprops.h (INT_PROMOTE): New macro. + * tests/test-intprops.c: Test it. + 2025-02-05 Paul Eggert errno-h: document Haiku errors can’t be -1 diff --git a/doc/intprops.texi b/doc/intprops.texi index bdb96c295d..1251f3d3c5 100644 --- a/doc/intprops.texi +++ b/doc/intprops.texi @@ -49,6 +49,7 @@ but it does not assume that signed integer arithmetic wraps around. @menu * Arithmetic Type Properties:: Determining properties of arithmetic types. +* Arithmetic Type Conversion:: Converting arithmetic types. * Integer Bounds:: Bounds on integer values and representations. * Checking Integer Overflow:: Checking for overflow while computing integers. * Wraparound Arithmetic:: Well-defined behavior on integer overflow. @@ -99,6 +100,62 @@ CLOCKS_PER_SEC_is_signed (void) @} @end example +@node Arithmetic Type Conversion +@subsection Arithmetic Type Conversion + +@cindex type conversion, arithmetic +@cindex arithmetic type conversion +@cindex integer type conversion + +Here are some ways in C to convert an arithmetic expression @var{e} to +a possibly different arithmetic type @var{t}. + +@itemize @bullet +@item +An explicit conversion like @code{((@var{t}) @var{e})} is powerful and +can therefore be dangerous, because the conversion can succeed even if +neither @var{e} nor @var{t} happens to have an arithmetic type. For +example, if @var{e} is a pointer, @code{((long int) @var{e})} will do +a conversion even if that was not the intent. + +@item +An implicit conversion like @code{@var{t} v = @var{e};} is less +powerful, as it does not convert from pointers. However, it can lose +information and sign, as in for example @code{int v = -0.9;} which +sets @code{v} to zero. + +@item +A no-op arithmetic expression like @code{+@var{e}} is even less +powerful, as it preserves value (including sign) because it does only +integer promotions. That is, it converts to @code{int} if that can +represent all values of @code{e}'s underlying type, otherwise to +@code{unsigned int} if that can represent all values, and +otherwise it does no conversion. +@end itemize + +@findex INT_PROMOTE +@code{INT_PROMOTE (@var{e})} is an expression with the same value as +the arithmetic expression @var{e} but with @var{e}'s type after +any integer promotion. It behaves like @code{+@var{e}}. + +In the following example, using @code{INT_PROMOTE} pacifies GCC's +@code{-Wswitch-enum} option, and may help human readers see what is +going on even if they are not expert in C's integer promotion rules +and might be confused by the simpler @code{switch (+v)}. + +@example +enum @{ A = 1, B, C, D, E @} v = ...; +switch (INT_PROMOTE (v)) + @{ + case A: case C: + return true; + default: + /* Handle all other cases, + even cases like v == 0. */ + return false; + @} +@end example + @node Integer Bounds @subsection Integer Bounds diff --git a/lib/intprops.h b/lib/intprops.h index 92dfef2500..83efe39910 100644 --- a/lib/intprops.h +++ b/lib/intprops.h @@ -34,6 +34,14 @@ signed or floating type. Do not evaluate E. */ #define EXPR_SIGNED(e) _GL_EXPR_SIGNED (e) +/* The same value as as the arithmetic expression E, but with E's type + after integer promotions. For example, if E is of type 'enum {A, B}' + then 'switch (INT_PROMOTE (E))' pacifies gcc -Wswitch-enum if some + enum values are deliberately omitted from the switch's cases. + Here, unary + is safer than a cast or inline function, as unary + + does only integer promotions. */ +#define INT_PROMOTE(e) (+ (e)) + /* Minimum and maximum values for integer types and expressions. */ diff --git a/tests/test-intprops.c b/tests/test-intprops.c index e3f165f146..026c615aba 100644 --- a/tests/test-intprops.c +++ b/tests/test-intprops.c @@ -62,6 +62,14 @@ #define DONTCARE __LINE__ +/* Check that INT_PROMOTE promotes to int. + GCC < 4.9 lacks _Generic even though it may claim C11 conformance. */ +#if (201112 <= __STDC_VERSION__ \ + && (!defined __GNUC__ || 4 < __GNUC__ + (9 <= __GNUC_MINOR__) \ + || defined __clang__)) +int check_INT_PROMOTE = _Generic (INT_PROMOTE ((short int) 0), int: 0); +#endif + int int_minus_2 = -2; int int_1 = 1;