From 5edd13bdcf2274967a7d4a7df2f86f6bddb5fac2 Mon Sep 17 00:00:00 2001 From: namniav Date: Sat, 20 Apr 2024 20:38:09 +0800 Subject: [PATCH] Fix Py_BUILD_ASSERT on non-constant expression --- Include/cpython/pyatomic_msc.h | 2 +- Include/internal/pycore_bitutils.h | 10 ++-- Include/internal/pycore_pyhash.h | 4 +- Include/pymacro.h | 50 ++++++++++++++++--- ...-04-20-14-05-12.gh-issue-118124.K-TPl4.rst | 3 ++ Modules/_ctypes/_ctypes.c | 2 +- Modules/_ssl.c | 6 +-- Modules/_testcapi/hash.c | 4 +- Modules/_testcapi/time.c | 4 +- Modules/_testcapimodule.c | 16 +++++- Modules/_testinternalcapi.c | 2 +- Modules/timemodule.c | 2 +- Python/pytime.c | 6 +-- 13 files changed, 83 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-04-20-14-05-12.gh-issue-118124.K-TPl4.rst diff --git a/Include/cpython/pyatomic_msc.h b/Include/cpython/pyatomic_msc.h index 212cd7817d01c5..ed989818727992 100644 --- a/Include/cpython/pyatomic_msc.h +++ b/Include/cpython/pyatomic_msc.h @@ -16,7 +16,7 @@ #include #define _Py_atomic_ASSERT_ARG_TYPE(TYPE) \ - Py_BUILD_ASSERT(sizeof(*obj) == sizeof(TYPE)) + static_assert(sizeof(*obj) == sizeof(TYPE), "") // --- _Py_atomic_add -------------------------------------------------------- diff --git a/Include/internal/pycore_bitutils.h b/Include/internal/pycore_bitutils.h index 50f69377523818..62d12b735c67e9 100644 --- a/Include/internal/pycore_bitutils.h +++ b/Include/internal/pycore_bitutils.h @@ -36,7 +36,7 @@ _Py_bswap16(uint16_t word) #if defined(_PY_HAVE_BUILTIN_BSWAP) || _Py__has_builtin(__builtin_bswap16) return __builtin_bswap16(word); #elif defined(_MSC_VER) - Py_BUILD_ASSERT(sizeof(word) == sizeof(unsigned short)); + static_assert(sizeof(word) == sizeof(unsigned short), ""); return _byteswap_ushort(word); #else // Portable implementation which doesn't rely on circular bit shift @@ -51,7 +51,7 @@ _Py_bswap32(uint32_t word) #if defined(_PY_HAVE_BUILTIN_BSWAP) || _Py__has_builtin(__builtin_bswap32) return __builtin_bswap32(word); #elif defined(_MSC_VER) - Py_BUILD_ASSERT(sizeof(word) == sizeof(unsigned long)); + static_assert(sizeof(word) == sizeof(unsigned long), ""); return _byteswap_ulong(word); #else // Portable implementation which doesn't rely on circular bit shift @@ -97,12 +97,12 @@ _Py_popcount32(uint32_t x) #if (defined(__clang__) || defined(__GNUC__)) #if SIZEOF_INT >= 4 - Py_BUILD_ASSERT(sizeof(x) <= sizeof(unsigned int)); + static_assert(sizeof(x) <= sizeof(unsigned int), ""); return __builtin_popcount(x); #else // The C standard guarantees that unsigned long will always be big enough // to hold a uint32_t value without losing information. - Py_BUILD_ASSERT(sizeof(x) <= sizeof(unsigned long)); + static_assert(sizeof(x) <= sizeof(unsigned long), ""); return __builtin_popcountl(x); #endif @@ -156,7 +156,7 @@ _Py_bit_length(unsigned long x) } #elif defined(_MSC_VER) // _BitScanReverse() is documented to search 32 bits. - Py_BUILD_ASSERT(sizeof(unsigned long) <= 4); + static_assert(sizeof(unsigned long) <= 4, ""); unsigned long msb; if (_BitScanReverse(&msb, x)) { return (int)msb + 1; diff --git a/Include/internal/pycore_pyhash.h b/Include/internal/pycore_pyhash.h index 0ce08900e96f0b..d6495811169cc9 100644 --- a/Include/internal/pycore_pyhash.h +++ b/Include/internal/pycore_pyhash.h @@ -10,13 +10,13 @@ static inline Py_hash_t _Py_HashPointerRaw(const void *ptr) { uintptr_t x = (uintptr_t)ptr; - Py_BUILD_ASSERT(sizeof(x) == sizeof(ptr)); + static_assert(sizeof(x) == sizeof(ptr), ""); // Bottom 3 or 4 bits are likely to be 0; rotate x by 4 to the right // to avoid excessive hash collisions for dicts and sets. x = (x >> 4) | (x << (8 * sizeof(uintptr_t) - 4)); - Py_BUILD_ASSERT(sizeof(x) == sizeof(Py_hash_t)); + static_assert(sizeof(x) == sizeof(Py_hash_t), ""); return (Py_hash_t)x; } diff --git a/Include/pymacro.h b/Include/pymacro.h index cd6fc4eba9c2ed..637304b95e371b 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -40,6 +40,13 @@ by "__LINE__". */ #define Py_STRINGIFY(x) _Py_XSTRINGIFY(x) +#define _Py_CONCAT(x, y) x##y + +/* Concatenate the arguments. An indirection is required for expanding + arguments once. For example Py_CONCAT(name, __LINE__) is replaced by name + followed by the line number, not by "__LINE__". */ +#define Py_CONCAT(x, y) _Py_CONCAT(x, y) + /* Get the size of a structure member in bytes */ #define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member) @@ -56,13 +63,44 @@ #define foo_to_char(foo) \ ((char *)(foo) \ + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) + Originaly written by Rusty Russell, public domain, http://ccodearchive.net/ + Modified to prohibit variable-length arrays(VLA). */ +#if defined(__cplusplus) + template + struct _Py_BUILD_ASSERT_EXPR_prohibit_vla { + static_assert(sizeof(T) == 1, + "Py_BUILD_ASSERT_EXPR can only be used with constant " + "expression of value true"); + }; +# define Py_BUILD_ASSERT_EXPR(cond) \ + (!sizeof(_Py_BUILD_ASSERT_EXPR_prohibit_vla)) +#elif defined(_MSC_VER) +# define Py_BUILD_ASSERT_EXPR(cond) \ + (!sizeof( \ + __pragma(warning(push)) \ + __pragma(warning(suppress: 4116)) \ + enum { \ + Py_CONCAT(_Py_BUILD_ASSERT_EXPR_prohibit_vla_,__LINE__) = \ + sizeof(char[1 - 2 * !(cond)]) \ + } \ + __pragma(warning(pop)) \ + )) +#else +# define Py_BUILD_ASSERT_EXPR(cond) \ + (!sizeof( \ + enum { \ + Py_CONCAT(_Py_BUILD_ASSERT_EXPR_prohibit_vla_,__LINE__) = \ + sizeof(char[1 - 2 * !(cond)]) \ + } \ + )) +#endif - Written by Rusty Russell, public domain, http://ccodearchive.net/ */ -#define Py_BUILD_ASSERT_EXPR(cond) \ - (sizeof(char [1 - 2*!(cond)]) - 1) - -#define Py_BUILD_ASSERT(cond) do { \ - (void)Py_BUILD_ASSERT_EXPR(cond); \ +/* Deprecated, use static_assert instead. */ +#define Py_BUILD_ASSERT(cond) do { \ + Py_DEPRECATED(3.13) \ + int _Py_BUILD_ASSERT_is_deprecated_use_static_assert_instead = 0; \ + _Py_BUILD_ASSERT_is_deprecated_use_static_assert_instead; \ + static_assert(cond, ""); \ } while(0) /* Get the number of elements in a visible array diff --git a/Misc/NEWS.d/next/C API/2024-04-20-14-05-12.gh-issue-118124.K-TPl4.rst b/Misc/NEWS.d/next/C API/2024-04-20-14-05-12.gh-issue-118124.K-TPl4.rst new file mode 100644 index 00000000000000..190da7141f8916 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-04-20-14-05-12.gh-issue-118124.K-TPl4.rst @@ -0,0 +1,3 @@ +Deprecates :c:macro:`Py_BUILD_ASSERT`. Passing non-constant expression to +:c:macro:`Py_BUILD_ASSERT` or :c:macro:`Py_BUILD_ASSERT_EXPR` now makes +compilation failed. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 3cb0b24668eb2a..38cb93d0f6f194 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2750,7 +2750,7 @@ unique_key(CDataObject *target, Py_ssize_t index) char *cp = string; size_t bytes_left; - Py_BUILD_ASSERT(sizeof(string) - 1 > sizeof(Py_ssize_t) * 2); + static_assert(sizeof(string) - 1 > sizeof(Py_ssize_t) * 2, ""); cp += sprintf(cp, "%x", Py_SAFE_DOWNCAST(index, Py_ssize_t, int)); while (target->b_base) { bytes_left = sizeof(string) - (cp - string) - 1; diff --git a/Modules/_ssl.c b/Modules/_ssl.c index f7fdbf4b6f90cb..f5ca6885684345 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -3620,7 +3620,7 @@ static PyObject * get_options(PySSLContext *self, void *c) { uint64_t options = SSL_CTX_get_options(self->ctx); - Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(options)); + static_assert(sizeof(unsigned long long) >= sizeof(options), ""); return PyLong_FromUnsignedLongLong(options); } @@ -3642,7 +3642,7 @@ set_options(PySSLContext *self, PyObject *arg, void *c) if (new_opts_arg == (unsigned long long)-1 && PyErr_Occurred()) { return -1; } - Py_BUILD_ASSERT(sizeof(new_opts) >= sizeof(new_opts_arg)); + static_assert(sizeof(new_opts) >= sizeof(new_opts_arg), ""); new_opts = (uint64_t)new_opts_arg; opts = SSL_CTX_get_options(self->ctx); @@ -6062,7 +6062,7 @@ sslmodule_init_socketapi(PyObject *module) static int sslmodule_add_option(PyObject *m, const char *name, uint64_t value) { - Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(value)); + static_assert(sizeof(unsigned long long) >= sizeof(value), ""); return PyModule_Add(m, name, PyLong_FromUnsignedLongLong(value)); } diff --git a/Modules/_testcapi/hash.c b/Modules/_testcapi/hash.c index 809d537bfef0d3..fe81b427c407d9 100644 --- a/Modules/_testcapi/hash.c +++ b/Modules/_testcapi/hash.c @@ -54,7 +54,7 @@ hash_pointer(PyObject *Py_UNUSED(module), PyObject *arg) } Py_hash_t hash = Py_HashPointer(ptr); - Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash)); + static_assert(sizeof(long long) >= sizeof(hash), ""); return PyLong_FromLongLong(hash); } @@ -64,7 +64,7 @@ object_generichash(PyObject *Py_UNUSED(module), PyObject *arg) { NULLABLE(arg); Py_hash_t hash = PyObject_GenericHash(arg); - Py_BUILD_ASSERT(sizeof(long long) >= sizeof(hash)); + static_assert(sizeof(long long) >= sizeof(hash), ""); return PyLong_FromLongLong(hash); } diff --git a/Modules/_testcapi/time.c b/Modules/_testcapi/time.c index 68f082bf3f3d88..8a05a252aded0d 100644 --- a/Modules/_testcapi/time.c +++ b/Modules/_testcapi/time.c @@ -15,7 +15,7 @@ pytime_from_nanoseconds(PyTime_t *tp, PyObject *obj) return -1; } - Py_BUILD_ASSERT(sizeof(long long) == sizeof(PyTime_t)); + static_assert(sizeof(long long) == sizeof(PyTime_t), ""); *tp = (PyTime_t)nsec; return 0; } @@ -98,7 +98,7 @@ _PyTestCapi_Init_Time(PyObject *m) if (PyModule_AddFunctions(m, test_methods) < 0) { return -1; } - Py_BUILD_ASSERT(sizeof(long long) == sizeof(PyTime_t)); + static_assert(sizeof(long long) == sizeof(PyTime_t), ""); if (PyModule_AddObject(m, "PyTime_MIN", PyLong_FromLongLong(PyTime_MIN)) < 0) { return 1; } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 034a30fa47ed30..174087f9ddaf6b 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2905,8 +2905,22 @@ test_macros(PyObject *self, PyObject *Py_UNUSED(args)) // static_assert(), Py_BUILD_ASSERT() static_assert(1 == 1, "bug"); + // Py_BUILD_ASSERT is now deprecated +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# pragma GCC diagnostic ignored "-Wunused-value" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4996) +# pragma warning(disable:4555) +#endif Py_BUILD_ASSERT(1 == 1); - +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif // Py_MIN(), Py_MAX(), Py_ABS() assert(Py_MIN(5, 11) == 5); diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index cc9e1403f87ecd..b2165aa6ff5aa4 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1931,7 +1931,7 @@ static PyObject * get_py_thread_id(PyObject *self, PyObject *Py_UNUSED(ignored)) { uintptr_t tid = _Py_ThreadId(); - Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(tid)); + static_assert(sizeof(unsigned long long) >= sizeof(tid), ""); return PyLong_FromUnsignedLongLong(tid); } #endif diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 3211c7530da0b2..857e39e41f11bd 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -198,7 +198,7 @@ time_clockid_converter(PyObject *obj, clockid_t *p) } // Make sure that we picked the right type (check sizes type) - Py_BUILD_ASSERT(sizeof(clk_id) == sizeof(*p)); + static_assert(sizeof(clk_id) == sizeof(*p), ""); *p = (clockid_t)clk_id; return 1; } diff --git a/Python/pytime.c b/Python/pytime.c index d5b38047b6db31..d02cc607d9b4ed 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -1046,7 +1046,7 @@ py_win_perf_counter_frequency(_PyTimeFraction *base, int raise_exc) // Since Windows XP, frequency cannot be zero. assert(frequency >= 1); - Py_BUILD_ASSERT(sizeof(PyTime_t) == sizeof(frequency)); + static_assert(sizeof(PyTime_t) == sizeof(frequency), ""); PyTime_t denom = (PyTime_t)frequency; // Known QueryPerformanceFrequency() values: @@ -1113,8 +1113,8 @@ py_mach_timebase_info(_PyTimeFraction *base, int raise_exc) // PyTime_t. In practice, timebase uses uint32_t, so casting cannot // overflow. At the end, only make sure that the type is uint32_t // (PyTime_t is 64-bit long). - Py_BUILD_ASSERT(sizeof(timebase.numer) <= sizeof(PyTime_t)); - Py_BUILD_ASSERT(sizeof(timebase.denom) <= sizeof(PyTime_t)); + static_assert(sizeof(timebase.numer) <= sizeof(PyTime_t), ""); + static_assert(sizeof(timebase.denom) <= sizeof(PyTime_t), ""); PyTime_t numer = (PyTime_t)timebase.numer; PyTime_t denom = (PyTime_t)timebase.denom;