/** ** copyright © 2014, Jens Gustedt ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** ** 2. Redistributions in binary form must reproduce the above ** copyright notice, this list of conditions and the following ** disclaimer in the documentation and/or other materials provided ** with the distribution. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND ** CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS ** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ** ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ** TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF ** THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. **/ /** ** @file ** @brief Test C standard numerical macros for their size and type ** ** The C standard imposes types for a lot of numerical values, such ** as minimum and maximum values of certain types. ** ** Before C11, only the size and sometimes the signedness of a ** integer or floating point literal have been observable. With C11 ** the type itself becomes observable through _Generic ** expressions. Portability requires then that the types of the ** macros are exactly what the standard defines. ** ** There are basically three forms of restrictions by the ** standard. ** ** The first is an exact definition of integer constants, e.g ** UINT16_C(1) must be of type ** uint_least16_t. These must all be suitable for ** evaluation in the preprocessor, so we also test them for that. ** ** The second form is a prescription of the promoted type. These are ** e.g the minimum and maximum values of signed integer types. So the ** promotion rules apply for them: for narrow types the resulting ** type for the constant is int (most of the times) for wide types it ** that same wide types. E.g SHORT_MAX is usually of ** type int, LONG_MAX is long. ** They also must be suitable for evaluation in the preprocessor, so ** we also test them for that. ** ** The third is an exact definition of floating point constants, e.g ** _Complex_I must be of type _Complex ** double. These can't be used in the preprocessor, so we ** don't test that, but must be suitable in initializers of static ** variables. So we test for that. ** ** This program implements tests on two different levels. The first ** should work with any C99 compatible compiler. It checks that the ** sizes are correct, for signedness when that is possible, that is ** when the type doesn't promote, and if the constant is suitable for ** initialization of static objects. ** ** When compiled with a C11 compiler and additional check for the ** type is made. The program prints all findings and a summary. ** ** @return is @c EXIT_SUCCESS if all sizes and types were ** correct. Otherwise @c EXIT_FAILURE is returned. **/ #ifndef __STDC_NO_COMPLEX__ # include #endif #include #include #include #include #include #include #include #include #include #include /* some utilities to use with _Generic for type identification */ enum cls { c_def, c_b, c_c, c_hhi, c_hhu, c_hi, c_hu, c_i, c_u, c_li, c_lu, c_lli, c_llu, c_f, c_lf, c_llf, c_cf, c_clf, c_cllf, }; #ifndef __STDC_NO_COMPLEX__ # define COMPLEX_TYPES _Complex float: c_cf, _Complex double: c_clf, _Complex long double: c_cllf #else # define COMPLEX_TYPES #endif #define TYPEID(X) \ _Generic((X), \ default: c_def, \ _Bool: c_b, \ char: c_c, signed char: c_hhi, unsigned char: c_hhu, \ signed short: c_hi, unsigned short: c_hu, \ signed: c_i, unsigned: c_u, \ signed long: c_li, unsigned long: c_lu, \ signed long long: c_lli, unsigned long long: c_llu, \ float: c_f, double: c_lf, long double: c_llf, \ COMPLEX_TYPES) #define TYPENAME(X) \ (char const*[]){ \ [c_def] = "", \ [c_b] = "_Bool", \ [c_c] = "char", \ [c_hhi] = "signed char", \ [c_hhu] = "unsigned char", \ [c_hi] = "signed short int", \ [c_hu] = "unsigned short int", \ [c_i] = "signed int", \ [c_u] = "unsigned int", \ [c_li] = "signed long int", \ [c_lu] = "unsigned long int", \ [c_lli] = "signed long long int", \ [c_llu] = "unsigned long long int", \ [c_f] = "float", \ [c_lf] = "double", \ [c_llf] = "long double", \ [c_cf] = "_Complex float", \ [c_clf] = "_Complex double", \ [c_cllf] = "_Complex long double", \ }[(X)] /* Counters to track the number of bugs. */ static size_t size_right; static size_t size_wrong; static size_t sign_right; static size_t sign_wrong; static size_t type_right; static size_t type_wrong; static size_t missing; static size_t mismatch; #define CHECK_EXACT_SIZE(T, X, ST, SX) \ do { \ if (sizeof(X) == sizeof(T)) { \ ++size_right; \ printf("%20s, %20s:\tright size\n", SX, ST); \ } else { \ ++size_wrong; \ printf("%20s, %20s:\twrong size, %s, %zu != %zu\n", \ SX, ST, #X, sizeof(X), sizeof(T)); \ } \ } while(0) #define CHECK_EXACT_TYPE(T, X, ST, SX) \ do { \ int type_x = TYPEID(X); \ int type_unpromoted = TYPEID((T)0); \ if (_Generic((X), T: 1, default: 0)) { \ ++type_right; \ printf("%20s, %20s:\tright type \"%s\"\n", SX, ST, \ TYPENAME(type_x)); \ } else { \ ++type_wrong; \ printf("%20s, %20s:\twrong type \"%s\", should be \"%s\"\n", \ SX, ST, TYPENAME(type_x), TYPENAME(type_unpromoted)); \ } \ } while(0) #define CHECK_SIGN(T, X, ST, SX) \ do { \ _Bool ispromoted = (sizeof(T) != sizeof(+(T)0)); \ if (!ispromoted) { \ _Bool issinged_T = (0 > (T)-1); \ _Bool issinged_x = (0 > (1 ? -1 : (X))); \ if (issinged_x == issinged_T) { \ ++sign_right; \ } else { \ ++sign_wrong; \ printf("%20s, %20s:\twrong sign, %ssigned instead of %ssigned\n", \ SX, ST, \ (issinged_x ? "" : "un"), \ (issinged_T ? "" : "un")); \ } \ } \ } while(0) #if !defined(SKIP_GENERIC) && __STDC_VERSION__ > 201000L # define CHECK_EXACT(T, X) \ do { \ CHECK_EXACT_SIZE(T, X, #T, #X); \ CHECK_SIGN(T, X, #T, #X); \ CHECK_EXACT_TYPE(T, X, #T, #X); \ } while (0) #else # define CHECK_EXACT(T, X) \ do { \ CHECK_EXACT_SIZE(T, X, #T, #X); \ CHECK_SIGN(T, X, #T, #X); \ } while (0) #endif #define CHECK_CONSTANT(T, X, ST, SX) \ do { \ static T volatile const obj = X; \ (void)obj; \ } while (0) #if !defined(SKIP_GENERIC) && __STDC_VERSION__ > 201000L # define CHECK_UNORDERED(T, X) \ do { \ CHECK_CONSTANT(T, X, #T, #X); \ CHECK_EXACT_SIZE(T, X, #T, #X); \ CHECK_EXACT_TYPE(T, X, #T, #X); \ } while (0) #else # define CHECK_UNORDERED(T, X) \ do { \ CHECK_CONSTANT(T, X, #T, #X); \ CHECK_EXACT_SIZE(T, X, #T, #X); \ } while (0) #endif #define CHECK_PROMOTED_SIZE(T, X, ST, SX) \ do { \ if (sizeof(X) == sizeof(+(T)0)) { \ ++size_right; \ printf("%20s, %20s:\tright size\n", SX, ST); \ } else { \ ++size_wrong; \ printf("%20s, %20s:\twrong size, %s, %zu != %zu\n", \ SX, ST, #X, sizeof(X), sizeof(+(T)0)); \ } \ } while(0) #define CHECK_PROMOTED_TYPE(T, X, ST, SX) \ do { \ bool unpromoted = _Generic(+(T)0, T: true, default: false); \ int type_x = TYPEID(X); \ int type_promoted = TYPEID(+(T)0); \ int type_unpromoted = TYPEID((T)0); \ bool correct = (unpromoted \ /* an unpromoted type must match */ \ ? _Generic((X), T: 1, default: 0) \ /* an promoted type mustn't */ \ : type_x == type_promoted); \ if (correct) { \ ++type_right; \ printf("%20s, %20s:\tright %spromoted type \"%s\"\n", SX, ST, \ (unpromoted ? "un" : ""), \ TYPENAME(type_promoted)); \ } else { \ ++type_wrong; \ printf("%20s, %20s:\twrong type, has type \"%s\" should be %spromoted type \"%s\"\n", \ SX, ST, TYPENAME(type_x), \ (unpromoted ? "un" : ""), \ (unpromoted \ ? TYPENAME(type_unpromoted) \ : TYPENAME(type_promoted))); \ } \ } while(0) #if !defined(SKIP_GENERIC) && __STDC_VERSION__ > 201000L # define CHECK_PROMOTED(T, X) \ do { \ CHECK_PROMOTED_SIZE(T, X, #T, #X); \ CHECK_SIGN(T, X, #T, #X); \ CHECK_PROMOTED_TYPE(T, X, #T, #X); \ } while (0) #else # define CHECK_PROMOTED(T, X) \ do { \ CHECK_PROMOTED_SIZE(T, X, #T, #X); \ CHECK_SIGN(T, X, #T, #X); \ } while (0) #endif #define TCMPLX(X, Y) (+(union { _Complex double _c; double _p[2]; }){ ._p = { [0] = (X), [1] = (Y) }}._c) #define CHECK_COMPLEX(X, Y) \ do { \ _Complex double A = CMPLX((X), (Y)); \ _Complex double B = TCMPLX((X), (Y)); \ if (A != B) { \ ++mismatch; \ printf("complex (%s, %s): (%g, %g) != (%g, %g)\n", \ #X, #Y, creal(A), cimag(A), creal(B), cimag(B)); \ } \ } while(0) int main(void) { /* types that must be unsigned */ #if false+1 CHECK_PROMOTED(bool, false); #endif #if true+1 CHECK_PROMOTED(bool, true); #endif #if UINT8_C(1) > 0 CHECK_EXACT(uint_least8_t, UINT8_C(1)); #endif #if UINT16_C(1) > 0 CHECK_EXACT(uint_least16_t, UINT16_C(1)); #endif #if UINT32_C(1) > 0 CHECK_EXACT(uint_least32_t, UINT32_C(1)); #endif #if UINT64_C(1) > 0 CHECK_EXACT(uint_least64_t, UINT64_C(1)); #endif #if UCHAR_MAX > 0 CHECK_PROMOTED(unsigned char, UCHAR_MAX); #endif #if USHRT_MAX > 0 CHECK_PROMOTED(unsigned short, USHRT_MAX); #endif #if UINT_MAX > 0 CHECK_EXACT(unsigned int, UINT_MAX); #endif #if ULONG_MAX > 0 CHECK_EXACT(unsigned long, ULONG_MAX); #endif #if ULLONG_MAX > 0 CHECK_EXACT(unsigned long long, ULLONG_MAX); #endif #if UINT8_MAX > 0 CHECK_PROMOTED(uint8_t, UINT8_MAX); #endif #if UINT16_MAX > 0 CHECK_PROMOTED(uint16_t, UINT16_MAX); #endif #if UINT32_MAX > 0 CHECK_PROMOTED(uint32_t, UINT32_MAX); #endif #if UINT64_MAX > 0 CHECK_PROMOTED(uint64_t, UINT64_MAX); #endif #if UINT_LEAST8_MAX > 0 CHECK_PROMOTED(uint_least8_t, UINT_LEAST8_MAX); #endif #if UINT_LEAST16_MAX > 0 CHECK_PROMOTED(uint_least16_t, UINT_LEAST16_MAX); #endif #if UINT_LEAST32_MAX > 0 CHECK_PROMOTED(uint_least32_t, UINT_LEAST32_MAX); #endif #if UINT_LEAST64_MAX > 0 CHECK_PROMOTED(uint_least64_t, UINT_LEAST64_MAX); #endif #if UINT_FAST8_MAX > 0 CHECK_PROMOTED(uint_fast8_t, UINT_FAST8_MAX); #endif #if UINT_FAST16_MAX > 0 CHECK_PROMOTED(uint_fast16_t, UINT_FAST16_MAX); #endif #if UINT_FAST32_MAX > 0 CHECK_PROMOTED(uint_fast32_t, UINT_FAST32_MAX); #endif #if UINT_FAST64_MAX > 0 CHECK_PROMOTED(uint_fast64_t, UINT_FAST64_MAX); #endif #if UINTMAX_MAX > 0 CHECK_EXACT(uintmax_t, UINTMAX_MAX); #endif #if UINPTR_MAX > 0 CHECK_PROMOTED(uintptr_t, UINTPTR_MAX); #endif #if SIZE_MAX > 0 CHECK_PROMOTED(size_t, SIZE_MAX); #endif /* types that that maybe signed */ #if INT8_C(1) > 0 CHECK_EXACT(int_least8_t, INT8_C(1)); #endif #if INT16_C(1) > 0 CHECK_EXACT(int_least16_t, INT16_C(1)); #endif #if INT32_C(1) > 0 CHECK_EXACT(int_least32_t, INT32_C(1)); #endif #if INT64_C(1) > 0 CHECK_EXACT(int_least64_t, INT64_C(1)); #endif #if CHAR_MAX > 0 CHECK_PROMOTED(char, CHAR_MAX); #endif #if SCHAR_MAX > 0 CHECK_PROMOTED(signed char, SCHAR_MAX); #endif #if SHRT_MAX > 0 CHECK_PROMOTED(signed short, SHRT_MAX); #endif #if INT_MAX > 0 CHECK_EXACT(signed int, INT_MAX); #endif #if LONG_MAX > 0 CHECK_EXACT(signed long, LONG_MAX); #endif #if LLONG_MAX > 0 CHECK_EXACT(signed long long, LLONG_MAX); #endif #if INT8_MAX > 0 CHECK_PROMOTED(int8_t, INT8_MAX); #endif #if INT16_MAX > 0 CHECK_PROMOTED(int16_t, INT16_MAX); #endif #if INT32_MAX > 0 CHECK_PROMOTED(int32_t, INT32_MAX); #endif #if INT64_MAX > 0 CHECK_PROMOTED(int64_t, INT64_MAX); #endif #if INT_LEAST8_MAX > 0 CHECK_PROMOTED(int_least8_t, INT_LEAST8_MAX); #endif #if INT_LEAST16_MAX > 0 CHECK_PROMOTED(int_least16_t, INT_LEAST16_MAX); #endif #if INT_LEAST32_MAX > 0 CHECK_PROMOTED(int_least32_t, INT_LEAST32_MAX); #endif #if INT_LEAST64_MAX > 0 CHECK_PROMOTED(int_least64_t, INT_LEAST64_MAX); #endif #if INT_FAST8_MAX > 0 CHECK_PROMOTED(int_fast8_t, INT_FAST8_MAX); #endif #if INT_FAST16_MAX > 0 CHECK_PROMOTED(int_fast16_t, INT_FAST16_MAX); #endif #if INT_FAST32_MAX > 0 CHECK_PROMOTED(int_fast32_t, INT_FAST32_MAX); #endif #if INT_FAST64_MAX > 0 CHECK_PROMOTED(int_fast64_t, INT_FAST64_MAX); #endif #if INTMAX_MAX > 0 CHECK_PROMOTED(intmax_t, INTMAX_MAX); #endif #if UINPTR_MAX > 0 CHECK_PROMOTED(intptr_t, INTPTR_MAX); #endif #if PTRDIFF_MAX > 0 CHECK_PROMOTED(ptrdiff_t, PTRDIFF_MAX); #endif #if SIG_ATOMIC_MAX > 0 CHECK_PROMOTED(sig_atomic_t, SIG_ATOMIC_MAX); #endif #if WCHAR_MAX > 0 CHECK_PROMOTED(wchar_t, WCHAR_MAX); #endif #if WINT_MAX > 0 /* wint_t should not promote */ CHECK_EXACT(wint_t, WINT_MAX); #endif #if CHAR_MIN < 1 CHECK_PROMOTED(char, CHAR_MIN); #endif #if SCHAR_MIN < 1 CHECK_PROMOTED(signed char, SCHAR_MIN); #endif #if SHRT_MIN < 1 CHECK_PROMOTED(signed short, SHRT_MIN); #endif #if INT_MIN < 1 CHECK_EXACT(signed int, INT_MIN); #endif #if LONG_MIN < 1 CHECK_EXACT(signed long, LONG_MIN); #endif #if LLONG_MIN < 1 CHECK_EXACT(signed long long, LLONG_MIN); #endif #if INT8_MIN < 1 CHECK_PROMOTED(int8_t, INT8_MIN); #endif #if INT16_MIN < 1 CHECK_PROMOTED(int16_t, INT16_MIN); #endif #if INT32_MIN < 1 CHECK_PROMOTED(int32_t, INT32_MIN); #endif #if INT64_MIN < 1 CHECK_PROMOTED(int64_t, INT64_MIN); #endif #if INT_LEAST8_MIN < 1 CHECK_PROMOTED(int_least8_t, INT_LEAST8_MIN); #endif #if INT_LEAST16_MIN < 1 CHECK_PROMOTED(int_least16_t, INT_LEAST16_MIN); #endif #if INT_LEAST32_MIN < 1 CHECK_PROMOTED(int_least32_t, INT_LEAST32_MIN); #endif #if INT_LEAST64_MIN < 1 CHECK_PROMOTED(int_least64_t, INT_LEAST64_MIN); #endif #if INT_FAST8_MIN < 1 CHECK_PROMOTED(int_fast8_t, INT_FAST8_MIN); #endif #if INT_FAST16_MIN < 1 CHECK_PROMOTED(int_fast16_t, INT_FAST16_MIN); #endif #if INT_FAST32_MIN < 1 CHECK_PROMOTED(int_fast32_t, INT_FAST32_MIN); #endif #if INT_FAST64_MIN < 1 CHECK_PROMOTED(int_fast64_t, INT_FAST64_MIN); #endif #if INTMAX_MIN < 1 CHECK_PROMOTED(intmax_t, INTMAX_MIN); #endif #if UINPTR_MIN < 1 CHECK_PROMOTED(intptr_t, INTPTR_MIN); #endif #if PTRDIFF_MIN < 1 CHECK_PROMOTED(ptrdiff_t, PTRDIFF_MIN); #endif #if SIG_ATOMIC_MIN < 1 CHECK_PROMOTED(sig_atomic_t, SIG_ATOMIC_MIN); #endif #if WCHAR_MIN < 1 CHECK_PROMOTED(wchar_t, WCHAR_MIN); #endif #if EOF < 1 || EOF > 0 CHECK_EXACT(int, EOF); #endif #if WINT_MIN < 1 /* wint_t should not promote */ CHECK_EXACT(wint_t, WINT_MIN); #endif #if WEOF < 1 || WEOF > 0 /* wint_t should not promote */ CHECK_EXACT(wint_t, WEOF); #endif CHECK_UNORDERED(float, FLT_MAX); CHECK_UNORDERED(float, FLT_MIN); CHECK_UNORDERED(float, FLT_TRUE_MIN); CHECK_UNORDERED(float, FLT_EPSILON); CHECK_UNORDERED(float, HUGE_VALF); CHECK_UNORDERED(float, INFINITY); #ifdef NAN CHECK_UNORDERED(float, NAN); #endif CHECK_UNORDERED(double, DBL_MAX); CHECK_UNORDERED(double, DBL_MIN); CHECK_UNORDERED(double, DBL_TRUE_MIN); CHECK_UNORDERED(double, DBL_EPSILON); CHECK_UNORDERED(double, HUGE_VAL); CHECK_UNORDERED(long double, LDBL_MAX); CHECK_UNORDERED(long double, LDBL_MIN); CHECK_UNORDERED(long double, LDBL_TRUE_MIN); CHECK_UNORDERED(long double, LDBL_EPSILON); CHECK_UNORDERED(long double, HUGE_VALL); #ifndef __STDC_NO_COMPLEX__ # ifdef imaginary CHECK_UNORDERED(_Imaginary float, I); # else CHECK_UNORDERED(_Complex float, I); # endif /* Notwithstanding the provisions of 7.1.3, a program may undefine and perhaps then redefine the macros complex, imaginary, and I. */ # undef imaginary # undef complex # undef I /* Try to invent the most stupid choices that an application could ever make. */ # define imaginary long double # define complex long double # define I 1.4L /* Now all other macros should still work. */ # ifdef _Imaginary_I CHECK_UNORDERED(_Imaginary float, _Imaginary_I); # endif CHECK_UNORDERED(_Complex float, _Complex_I); CHECK_UNORDERED(_Complex float, 1.0F + _Complex_I); CHECK_UNORDERED(_Complex double, 1.0 + _Complex_I); CHECK_UNORDERED(_Complex long double, 1.0L + _Complex_I); # ifdef CMPLXF CHECK_UNORDERED(_Complex float, CMPLXF(1.0L, 1.0L)); # else ++missing; puts("macro CMPLXF is missing"); # endif # ifdef CMPLX CHECK_UNORDERED(_Complex double, CMPLX(1.0L, 1.0L)); # else ++missing; puts("macro CMPLX is missing"); # endif # ifdef CMPLXL CHECK_UNORDERED(_Complex long double, CMPLXL(1.0F, 1.0F)); # else ++missing; puts("macro CMPLXL is missing"); # endif #endif puts("\ncheck a set of literals that all should work, independent of the C library"); CHECK_EXACT(int, 'a'); CHECK_EXACT(wchar_t, L'a'); #if __STDC_VERSION__ > 201000L CHECK_EXACT(uint_least16_t, u'a'); CHECK_EXACT(uint_least32_t, U'A'); #endif #ifdef __SIZEOF_INT128__ CHECK_EXACT(__int128, (__int128)0); #endif CHECK_UNORDERED(float, 0.0F); CHECK_UNORDERED(double, 0.0); CHECK_UNORDERED(long double, 0.0L); #ifdef CMPLX CHECK_COMPLEX(1.0, 0.0); CHECK_COMPLEX(0.0, 1.0); CHECK_COMPLEX(INFINITY, 0.0); CHECK_COMPLEX(0.0, INFINITY); #endif printf("\nSummary:\t%zu/%zu sizes wrong\n", size_wrong, size_wrong+size_right); printf("\t\t%zu/%zu signedness wrong\n", sign_wrong, sign_wrong+sign_right); #if __STDC_VERSION__ > 201000L printf("\t\t%zu/%zu types wrong\n", type_wrong, type_wrong+type_right); #endif printf("\t\t%zu macros are missing\n", missing); printf("\t\t%zu mismatches of complex values\n", mismatch); int ret = type_wrong + size_wrong + sign_wrong + missing + mismatch; puts("\ncheck a set of mismatches that should error, independent of the C library"); CHECK_UNORDERED(double, 0LL); CHECK_PROMOTED(double, 0LL); #ifdef __SIZEOF_INT128__ CHECK_EXACT(__int128, 0LL); #endif #if defined(NAN) && defined(CMPLX) puts("\nThe two following involve NAN, so comparison should always be false"); CHECK_COMPLEX(INFINITY, NAN); CHECK_COMPLEX(NAN, INFINITY); #endif return ret ? EXIT_FAILURE : EXIT_SUCCESS; }