Skip to content

GH-118124: Fix Py_BUILD_ASSERT on non-constant expression #118125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Include/cpython/pyatomic_msc.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include <intrin.h>

#define _Py_atomic_ASSERT_ARG_TYPE(TYPE) \
Py_BUILD_ASSERT(sizeof(*obj) == sizeof(TYPE))
static_assert(sizeof(*obj) == sizeof(TYPE), "")


// --- _Py_atomic_add --------------------------------------------------------
Expand Down
10 changes: 5 additions & 5 deletions Include/internal/pycore_bitutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_pyhash.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
50 changes: 44 additions & 6 deletions Include/pymacro.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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<typename T>
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<char[1 - 2 * !(cond)]>))
#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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
Expand Down Expand Up @@ -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));
}

Expand Down
4 changes: 2 additions & 2 deletions Modules/_testcapi/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down
4 changes: 2 additions & 2 deletions Modules/_testcapi/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
16 changes: 15 additions & 1 deletion Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Modules/timemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
6 changes: 3 additions & 3 deletions Python/pytime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;

Expand Down