From 7cdaf87ec50f76c934ba651256484c4624b84ef2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 20 Apr 2022 19:26:40 +0200 Subject: gh-91731: Replace Py_BUILD_ASSERT() with static_assert() (#91730) Python 3.11 now uses C11 standard which adds static_assert() to . * In pytime.c, replace Py_BUILD_ASSERT() with preprocessor checks on SIZEOF_TIME_T with #error. * On macOS, py_mach_timebase_info() now accepts timebase members with the same size than _PyTime_t. * py_get_monotonic_clock() now saturates GetTickCount64() to _PyTime_MAX: GetTickCount64() is unsigned, whereas _PyTime_t is signed. --- Modules/_datetimemodule.c | 6 ++--- Modules/_pickle.c | 2 +- Modules/_testcapimodule.c | 3 ++- Modules/posixmodule.c | 12 ++++++--- Modules/pyexpat.c | 4 ++- Objects/longobject.c | 7 ++++-- Objects/sliceobject.c | 3 ++- Python/fileutils.c | 2 +- Python/initconfig.c | 4 ++- Python/pytime.c | 63 ++++++++++++++++++++++++++++++----------------- 10 files changed, 68 insertions(+), 38 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index fc766c3..a35c723 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6637,19 +6637,19 @@ _datetime_exec(PyObject *module) /* A 4-year cycle has an extra leap day over what we'd get from * pasting together 4 single years. */ - Py_BUILD_ASSERT(DI4Y == 4 * 365 + 1); + static_assert(DI4Y == 4 * 365 + 1, "DI4Y"); assert(DI4Y == days_before_year(4+1)); /* Similarly, a 400-year cycle has an extra leap day over what we'd * get from pasting together 4 100-year cycles. */ - Py_BUILD_ASSERT(DI400Y == 4 * DI100Y + 1); + static_assert(DI400Y == 4 * DI100Y + 1, "DI400Y"); assert(DI400Y == days_before_year(400+1)); /* OTOH, a 100-year cycle has one fewer leap day than we'd get from * pasting together 25 4-year cycles. */ - Py_BUILD_ASSERT(DI100Y == 25 * DI4Y - 1); + static_assert(DI100Y == 25 * DI4Y - 1, "DI100Y"); assert(DI100Y == days_before_year(100+1)); us_per_ms = PyLong_FromLong(1000); diff --git a/Modules/_pickle.c b/Modules/_pickle.c index a5595eb..7a8c9ed 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -964,7 +964,7 @@ _write_size64(char *out, size_t value) { size_t i; - Py_BUILD_ASSERT(sizeof(size_t) <= 8); + static_assert(sizeof(size_t) <= 8, "size_t is larger than 64-bit"); for (i = 0; i < sizeof(size_t); i++) { out[i] = (unsigned char)((value >> (8 * i)) & 0xff); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 23af0d3..6bd73e8 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5157,7 +5157,8 @@ dict_get_version(PyObject *self, PyObject *args) version = dict->ma_version_tag; - Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(version)); + static_assert(sizeof(unsigned long long) >= sizeof(version), + "version is larger than unsigned long long"); return PyLong_FromUnsignedLongLong((unsigned long long)version); } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 700cbd2..345ed71 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -2381,7 +2381,8 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) return NULL; PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long)st->st_mode)); - Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(st->st_ino)); + static_assert(sizeof(unsigned long long) >= sizeof(st->st_ino), + "stat.st_ino is larger than unsigned long long"); PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(st->st_ino)); #ifdef MS_WINDOWS PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLong(st->st_dev)); @@ -2396,7 +2397,8 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) PyStructSequence_SET_ITEM(v, 4, _PyLong_FromUid(st->st_uid)); PyStructSequence_SET_ITEM(v, 5, _PyLong_FromGid(st->st_gid)); #endif - Py_BUILD_ASSERT(sizeof(long long) >= sizeof(st->st_size)); + static_assert(sizeof(long long) >= sizeof(st->st_size), + "stat.st_size is larger than long long"); PyStructSequence_SET_ITEM(v, 6, PyLong_FromLongLong(st->st_size)); #if defined(HAVE_STAT_TV_NSEC) @@ -13761,10 +13763,12 @@ _Py_COMP_DIAG_POP self->win32_file_index = stat.st_ino; self->got_file_index = 1; } - Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(self->win32_file_index)); + static_assert(sizeof(unsigned long long) >= sizeof(self->win32_file_index), + "DirEntry.win32_file_index is larger than unsigned long long"); return PyLong_FromUnsignedLongLong(self->win32_file_index); #else /* POSIX */ - Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(self->d_ino)); + static_assert(sizeof(unsigned long long) >= sizeof(self->d_ino), + "DirEntry.d_ino is larger than unsigned long long"); return PyLong_FromUnsignedLongLong(self->d_ino); #endif } diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 7a26fe2..ad8148a 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -745,6 +745,8 @@ pyexpat_xmlparser_Parse_impl(xmlparseobject *self, PyTypeObject *cls, slen = view.len; } + static_assert(MAX_CHUNK_SIZE <= INT_MAX, + "MAX_CHUNK_SIZE is larger than INT_MAX"); while (slen > MAX_CHUNK_SIZE) { rc = XML_Parse(self->itself, s, MAX_CHUNK_SIZE, 0); if (!rc) @@ -752,7 +754,7 @@ pyexpat_xmlparser_Parse_impl(xmlparseobject *self, PyTypeObject *cls, s += MAX_CHUNK_SIZE; slen -= MAX_CHUNK_SIZE; } - Py_BUILD_ASSERT(MAX_CHUNK_SIZE <= INT_MAX); + assert(slen <= INT_MAX); rc = XML_Parse(self->itself, s, (int)slen, isfinal); diff --git a/Objects/longobject.c b/Objects/longobject.c index 660824f..e805dac 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -771,7 +771,10 @@ _PyLong_Sign(PyObject *vv) static int bit_length_digit(digit x) { - Py_BUILD_ASSERT(PyLong_SHIFT <= sizeof(unsigned long) * 8); + // digit can be larger than unsigned long, but only PyLong_SHIFT bits + // of it will be ever used. + static_assert(PyLong_SHIFT <= sizeof(unsigned long) * 8, + "digit is larger than unsigned long"); return _Py_bit_length((unsigned long)x); } @@ -5647,7 +5650,7 @@ popcount_digit(digit d) { // digit can be larger than uint32_t, but only PyLong_SHIFT bits // of it will be ever used. - Py_BUILD_ASSERT(PyLong_SHIFT <= 32); + static_assert(PyLong_SHIFT <= 32, "digit is larger than uint32_t"); return _Py_popcount32((uint32_t)d); } diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 22fb7c6..713829d 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -206,7 +206,8 @@ PySlice_Unpack(PyObject *_r, PySliceObject *r = (PySliceObject*)_r; /* this is harder to get right than you might think */ - Py_BUILD_ASSERT(PY_SSIZE_T_MIN + 1 <= -PY_SSIZE_T_MAX); + static_assert(PY_SSIZE_T_MIN + 1 <= -PY_SSIZE_T_MAX, + "-PY_SSIZE_T_MAX < PY_SSIZE_T_MIN + 1"); if (r->step == Py_None) { *step = 1; diff --git a/Python/fileutils.c b/Python/fileutils.c index 582c6ba..4f7f894 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -956,7 +956,7 @@ static wchar_t * _Py_ConvertWCharForm(const wchar_t *source, Py_ssize_t size, const char *tocode, const char *fromcode) { - Py_BUILD_ASSERT(sizeof(wchar_t) == 4); + static_assert(sizeof(wchar_t) == 4, "wchar_t must be 32-bit"); /* Ensure we won't overflow the size. */ if (size > (PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(wchar_t))) { diff --git a/Python/initconfig.c b/Python/initconfig.c index d2e74f5..729f7f3 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -1507,9 +1507,11 @@ config_get_xoption_value(const PyConfig *config, wchar_t *name) static PyStatus config_init_hash_seed(PyConfig *config) { + static_assert(sizeof(_Py_HashSecret_t) == sizeof(_Py_HashSecret.uc), + "_Py_HashSecret_t has wrong size"); + const char *seed_text = config_get_env(config, "PYTHONHASHSEED"); - Py_BUILD_ASSERT(sizeof(_Py_HashSecret_t) == sizeof(_Py_HashSecret.uc)); /* Convert a text seed to a numeric one */ if (seed_text && strcmp(seed_text, "random") != 0) { const char *endptr = seed_text; diff --git a/Python/pytime.c b/Python/pytime.c index aff20c6..f49a25b 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -162,12 +162,11 @@ time_t _PyLong_AsTime_t(PyObject *obj) { #if SIZEOF_TIME_T == SIZEOF_LONG_LONG - long long val; - val = PyLong_AsLongLong(obj); + long long val = PyLong_AsLongLong(obj); +#elif SIZEOF_TIME_T <= SIZEOF_LONG + long val = PyLong_AsLong(obj); #else - long val; - Py_BUILD_ASSERT(sizeof(time_t) <= sizeof(long)); - val = PyLong_AsLong(obj); +# error "unsupported time_t size" #endif if (val == -1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { @@ -184,9 +183,10 @@ _PyLong_FromTime_t(time_t t) { #if SIZEOF_TIME_T == SIZEOF_LONG_LONG return PyLong_FromLongLong((long long)t); -#else - Py_BUILD_ASSERT(sizeof(time_t) <= sizeof(long)); +#elif SIZEOF_TIME_T <= SIZEOF_LONG return PyLong_FromLong((long)t); +#else +# error "unsupported time_t size" #endif } @@ -386,10 +386,10 @@ _PyTime_t _PyTime_FromSeconds(int seconds) { /* ensure that integer overflow cannot happen, int type should have 32 - bits, whereas _PyTime_t type has at least 64 bits (SEC_TO_MS takes 30 + bits, whereas _PyTime_t type has at least 64 bits (SEC_TO_NS takes 30 bits). */ - Py_BUILD_ASSERT(INT_MAX <= _PyTime_MAX / SEC_TO_NS); - Py_BUILD_ASSERT(INT_MIN >= _PyTime_MIN / SEC_TO_NS); + static_assert(INT_MAX <= _PyTime_MAX / SEC_TO_NS, "_PyTime_t overflow"); + static_assert(INT_MIN >= _PyTime_MIN / SEC_TO_NS, "_PyTime_t underflow"); _PyTime_t t = (_PyTime_t)seconds; assert((t >= 0 && t <= _PyTime_MAX / SEC_TO_NS) @@ -416,7 +416,8 @@ _PyTime_FromNanosecondsObject(_PyTime_t *tp, PyObject *obj) return -1; } - Py_BUILD_ASSERT(sizeof(long long) == sizeof(_PyTime_t)); + static_assert(sizeof(long long) == sizeof(_PyTime_t), + "_PyTime_t is not long long"); long long nsec = PyLong_AsLongLong(obj); if (nsec == -1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { @@ -437,7 +438,8 @@ pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise_exc) { _PyTime_t t, tv_nsec; - Py_BUILD_ASSERT(sizeof(ts->tv_sec) <= sizeof(_PyTime_t)); + static_assert(sizeof(ts->tv_sec) <= sizeof(_PyTime_t), + "timespec.tv_sec is larger than _PyTime_t"); t = (_PyTime_t)ts->tv_sec; int res1 = pytime_mul(&t, SEC_TO_NS); @@ -466,7 +468,8 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts) static int pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise_exc) { - Py_BUILD_ASSERT(sizeof(tv->tv_sec) <= sizeof(_PyTime_t)); + static_assert(sizeof(tv->tv_sec) <= sizeof(_PyTime_t), + "timeval.tv_sec is larger than _PyTime_t"); _PyTime_t t = (_PyTime_t)tv->tv_sec; int res1 = pytime_mul(&t, SEC_TO_NS); @@ -537,7 +540,8 @@ pytime_from_object(_PyTime_t *tp, PyObject *obj, _PyTime_round_t round, return -1; } - Py_BUILD_ASSERT(sizeof(long long) <= sizeof(_PyTime_t)); + static_assert(sizeof(long long) <= sizeof(_PyTime_t), + "_PyTime_t is smaller than long long"); _PyTime_t ns = (_PyTime_t)sec; if (pytime_mul(&ns, unit_to_ns) < 0) { pytime_overflow(); @@ -589,7 +593,8 @@ PyObject * _PyTime_AsNanosecondsObject(_PyTime_t t) { _PyTime_t ns = pytime_as_nanoseconds(t); - Py_BUILD_ASSERT(sizeof(long long) >= sizeof(_PyTime_t)); + static_assert(sizeof(long long) >= sizeof(_PyTime_t), + "_PyTime_t is larger than long long"); return PyLong_FromLongLong((long long)ns); } @@ -984,15 +989,17 @@ py_mach_timebase_info(_PyTime_t *pnumer, _PyTime_t *pdenom, int raise) _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), + "timebase.numer is larger than _PyTime_t"); + static_assert(sizeof(timebase.denom) <= sizeof(_PyTime_t), + "timebase.denom is larger than _PyTime_t"); - /* Make sure that (ticks * timebase.numer) cannot overflow in - _PyTime_MulDiv(), with ticks < timebase.denom. + /* Make sure that _PyTime_MulDiv(ticks, timebase_numer, timebase_denom) + cannot overflow. Known time bases: - * always (1, 1) on Intel + * (1, 1) on Intel * (1000000000, 33333335) or (1000000000, 25000000) on PowerPC None of these time bases can overflow with 64-bit _PyTime_t, but @@ -1019,8 +1026,17 @@ py_get_monotonic_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) #if defined(MS_WINDOWS) ULONGLONG ticks = GetTickCount64(); - Py_BUILD_ASSERT(sizeof(ticks) <= sizeof(_PyTime_t)); - _PyTime_t t = (_PyTime_t)ticks; + static_assert(sizeof(ticks) <= sizeof(_PyTime_t), + "ULONGLONG is larger than _PyTime_t"); + _PyTime_t t; + if (ticks <= (ULONGLONG)_PyTime_MAX) { + t = (_PyTime_t)ticks; + } + else { + // GetTickCount64() maximum is larger than _PyTime_t maximum: + // ULONGLONG is unsigned, whereas _PyTime_t is signed. + t = _PyTime_MAX; + } int res = pytime_mul(&t, MS_TO_NS); *tp = t; @@ -1211,7 +1227,8 @@ py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) /* Make sure that casting LONGLONG to _PyTime_t cannot overflow, both types are signed */ _PyTime_t ticks; - Py_BUILD_ASSERT(sizeof(ticksll) <= sizeof(ticks)); + static_assert(sizeof(ticksll) <= sizeof(ticks), + "LONGLONG is larger than _PyTime_t"); ticks = (_PyTime_t)ticksll; _PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)frequency); -- cgit v0.12