Skip to content

Commit 7cdaf87

Browse files
authored
gh-91731: Replace Py_BUILD_ASSERT() with static_assert() (#91730)
Python 3.11 now uses C11 standard which adds static_assert() to <assert.h>. * 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.
1 parent ad3ca17 commit 7cdaf87

10 files changed

+68
-38
lines changed

Diff for: Modules/_datetimemodule.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -6637,19 +6637,19 @@ _datetime_exec(PyObject *module)
66376637
/* A 4-year cycle has an extra leap day over what we'd get from
66386638
* pasting together 4 single years.
66396639
*/
6640-
Py_BUILD_ASSERT(DI4Y == 4 * 365 + 1);
6640+
static_assert(DI4Y == 4 * 365 + 1, "DI4Y");
66416641
assert(DI4Y == days_before_year(4+1));
66426642

66436643
/* Similarly, a 400-year cycle has an extra leap day over what we'd
66446644
* get from pasting together 4 100-year cycles.
66456645
*/
6646-
Py_BUILD_ASSERT(DI400Y == 4 * DI100Y + 1);
6646+
static_assert(DI400Y == 4 * DI100Y + 1, "DI400Y");
66476647
assert(DI400Y == days_before_year(400+1));
66486648

66496649
/* OTOH, a 100-year cycle has one fewer leap day than we'd get from
66506650
* pasting together 25 4-year cycles.
66516651
*/
6652-
Py_BUILD_ASSERT(DI100Y == 25 * DI4Y - 1);
6652+
static_assert(DI100Y == 25 * DI4Y - 1, "DI100Y");
66536653
assert(DI100Y == days_before_year(100+1));
66546654

66556655
us_per_ms = PyLong_FromLong(1000);

Diff for: Modules/_pickle.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,7 @@ _write_size64(char *out, size_t value)
964964
{
965965
size_t i;
966966

967-
Py_BUILD_ASSERT(sizeof(size_t) <= 8);
967+
static_assert(sizeof(size_t) <= 8, "size_t is larger than 64-bit");
968968

969969
for (i = 0; i < sizeof(size_t); i++) {
970970
out[i] = (unsigned char)((value >> (8 * i)) & 0xff);

Diff for: Modules/_testcapimodule.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -5157,7 +5157,8 @@ dict_get_version(PyObject *self, PyObject *args)
51575157

51585158
version = dict->ma_version_tag;
51595159

5160-
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(version));
5160+
static_assert(sizeof(unsigned long long) >= sizeof(version),
5161+
"version is larger than unsigned long long");
51615162
return PyLong_FromUnsignedLongLong((unsigned long long)version);
51625163
}
51635164

Diff for: Modules/posixmodule.c

+8-4
Original file line numberDiff line numberDiff line change
@@ -2381,7 +2381,8 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
23812381
return NULL;
23822382

23832383
PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long)st->st_mode));
2384-
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(st->st_ino));
2384+
static_assert(sizeof(unsigned long long) >= sizeof(st->st_ino),
2385+
"stat.st_ino is larger than unsigned long long");
23852386
PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(st->st_ino));
23862387
#ifdef MS_WINDOWS
23872388
PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLong(st->st_dev));
@@ -2396,7 +2397,8 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
23962397
PyStructSequence_SET_ITEM(v, 4, _PyLong_FromUid(st->st_uid));
23972398
PyStructSequence_SET_ITEM(v, 5, _PyLong_FromGid(st->st_gid));
23982399
#endif
2399-
Py_BUILD_ASSERT(sizeof(long long) >= sizeof(st->st_size));
2400+
static_assert(sizeof(long long) >= sizeof(st->st_size),
2401+
"stat.st_size is larger than long long");
24002402
PyStructSequence_SET_ITEM(v, 6, PyLong_FromLongLong(st->st_size));
24012403

24022404
#if defined(HAVE_STAT_TV_NSEC)
@@ -13761,10 +13763,12 @@ _Py_COMP_DIAG_POP
1376113763
self->win32_file_index = stat.st_ino;
1376213764
self->got_file_index = 1;
1376313765
}
13764-
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(self->win32_file_index));
13766+
static_assert(sizeof(unsigned long long) >= sizeof(self->win32_file_index),
13767+
"DirEntry.win32_file_index is larger than unsigned long long");
1376513768
return PyLong_FromUnsignedLongLong(self->win32_file_index);
1376613769
#else /* POSIX */
13767-
Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(self->d_ino));
13770+
static_assert(sizeof(unsigned long long) >= sizeof(self->d_ino),
13771+
"DirEntry.d_ino is larger than unsigned long long");
1376813772
return PyLong_FromUnsignedLongLong(self->d_ino);
1376913773
#endif
1377013774
}

Diff for: Modules/pyexpat.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -745,14 +745,16 @@ pyexpat_xmlparser_Parse_impl(xmlparseobject *self, PyTypeObject *cls,
745745
slen = view.len;
746746
}
747747

748+
static_assert(MAX_CHUNK_SIZE <= INT_MAX,
749+
"MAX_CHUNK_SIZE is larger than INT_MAX");
748750
while (slen > MAX_CHUNK_SIZE) {
749751
rc = XML_Parse(self->itself, s, MAX_CHUNK_SIZE, 0);
750752
if (!rc)
751753
goto done;
752754
s += MAX_CHUNK_SIZE;
753755
slen -= MAX_CHUNK_SIZE;
754756
}
755-
Py_BUILD_ASSERT(MAX_CHUNK_SIZE <= INT_MAX);
757+
756758
assert(slen <= INT_MAX);
757759
rc = XML_Parse(self->itself, s, (int)slen, isfinal);
758760

Diff for: Objects/longobject.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,10 @@ _PyLong_Sign(PyObject *vv)
771771
static int
772772
bit_length_digit(digit x)
773773
{
774-
Py_BUILD_ASSERT(PyLong_SHIFT <= sizeof(unsigned long) * 8);
774+
// digit can be larger than unsigned long, but only PyLong_SHIFT bits
775+
// of it will be ever used.
776+
static_assert(PyLong_SHIFT <= sizeof(unsigned long) * 8,
777+
"digit is larger than unsigned long");
775778
return _Py_bit_length((unsigned long)x);
776779
}
777780

@@ -5647,7 +5650,7 @@ popcount_digit(digit d)
56475650
{
56485651
// digit can be larger than uint32_t, but only PyLong_SHIFT bits
56495652
// of it will be ever used.
5650-
Py_BUILD_ASSERT(PyLong_SHIFT <= 32);
5653+
static_assert(PyLong_SHIFT <= 32, "digit is larger than uint32_t");
56515654
return _Py_popcount32((uint32_t)d);
56525655
}
56535656

Diff for: Objects/sliceobject.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ PySlice_Unpack(PyObject *_r,
206206
PySliceObject *r = (PySliceObject*)_r;
207207
/* this is harder to get right than you might think */
208208

209-
Py_BUILD_ASSERT(PY_SSIZE_T_MIN + 1 <= -PY_SSIZE_T_MAX);
209+
static_assert(PY_SSIZE_T_MIN + 1 <= -PY_SSIZE_T_MAX,
210+
"-PY_SSIZE_T_MAX < PY_SSIZE_T_MIN + 1");
210211

211212
if (r->step == Py_None) {
212213
*step = 1;

Diff for: Python/fileutils.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ static wchar_t *
956956
_Py_ConvertWCharForm(const wchar_t *source, Py_ssize_t size,
957957
const char *tocode, const char *fromcode)
958958
{
959-
Py_BUILD_ASSERT(sizeof(wchar_t) == 4);
959+
static_assert(sizeof(wchar_t) == 4, "wchar_t must be 32-bit");
960960

961961
/* Ensure we won't overflow the size. */
962962
if (size > (PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(wchar_t))) {

Diff for: Python/initconfig.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -1507,9 +1507,11 @@ config_get_xoption_value(const PyConfig *config, wchar_t *name)
15071507
static PyStatus
15081508
config_init_hash_seed(PyConfig *config)
15091509
{
1510+
static_assert(sizeof(_Py_HashSecret_t) == sizeof(_Py_HashSecret.uc),
1511+
"_Py_HashSecret_t has wrong size");
1512+
15101513
const char *seed_text = config_get_env(config, "PYTHONHASHSEED");
15111514

1512-
Py_BUILD_ASSERT(sizeof(_Py_HashSecret_t) == sizeof(_Py_HashSecret.uc));
15131515
/* Convert a text seed to a numeric one */
15141516
if (seed_text && strcmp(seed_text, "random") != 0) {
15151517
const char *endptr = seed_text;

Diff for: Python/pytime.c

+40-23
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,11 @@ time_t
162162
_PyLong_AsTime_t(PyObject *obj)
163163
{
164164
#if SIZEOF_TIME_T == SIZEOF_LONG_LONG
165-
long long val;
166-
val = PyLong_AsLongLong(obj);
165+
long long val = PyLong_AsLongLong(obj);
166+
#elif SIZEOF_TIME_T <= SIZEOF_LONG
167+
long val = PyLong_AsLong(obj);
167168
#else
168-
long val;
169-
Py_BUILD_ASSERT(sizeof(time_t) <= sizeof(long));
170-
val = PyLong_AsLong(obj);
169+
# error "unsupported time_t size"
171170
#endif
172171
if (val == -1 && PyErr_Occurred()) {
173172
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
@@ -184,9 +183,10 @@ _PyLong_FromTime_t(time_t t)
184183
{
185184
#if SIZEOF_TIME_T == SIZEOF_LONG_LONG
186185
return PyLong_FromLongLong((long long)t);
187-
#else
188-
Py_BUILD_ASSERT(sizeof(time_t) <= sizeof(long));
186+
#elif SIZEOF_TIME_T <= SIZEOF_LONG
189187
return PyLong_FromLong((long)t);
188+
#else
189+
# error "unsupported time_t size"
190190
#endif
191191
}
192192

@@ -386,10 +386,10 @@ _PyTime_t
386386
_PyTime_FromSeconds(int seconds)
387387
{
388388
/* ensure that integer overflow cannot happen, int type should have 32
389-
bits, whereas _PyTime_t type has at least 64 bits (SEC_TO_MS takes 30
389+
bits, whereas _PyTime_t type has at least 64 bits (SEC_TO_NS takes 30
390390
bits). */
391-
Py_BUILD_ASSERT(INT_MAX <= _PyTime_MAX / SEC_TO_NS);
392-
Py_BUILD_ASSERT(INT_MIN >= _PyTime_MIN / SEC_TO_NS);
391+
static_assert(INT_MAX <= _PyTime_MAX / SEC_TO_NS, "_PyTime_t overflow");
392+
static_assert(INT_MIN >= _PyTime_MIN / SEC_TO_NS, "_PyTime_t underflow");
393393

394394
_PyTime_t t = (_PyTime_t)seconds;
395395
assert((t >= 0 && t <= _PyTime_MAX / SEC_TO_NS)
@@ -416,7 +416,8 @@ _PyTime_FromNanosecondsObject(_PyTime_t *tp, PyObject *obj)
416416
return -1;
417417
}
418418

419-
Py_BUILD_ASSERT(sizeof(long long) == sizeof(_PyTime_t));
419+
static_assert(sizeof(long long) == sizeof(_PyTime_t),
420+
"_PyTime_t is not long long");
420421
long long nsec = PyLong_AsLongLong(obj);
421422
if (nsec == -1 && PyErr_Occurred()) {
422423
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
@@ -437,7 +438,8 @@ pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise_exc)
437438
{
438439
_PyTime_t t, tv_nsec;
439440

440-
Py_BUILD_ASSERT(sizeof(ts->tv_sec) <= sizeof(_PyTime_t));
441+
static_assert(sizeof(ts->tv_sec) <= sizeof(_PyTime_t),
442+
"timespec.tv_sec is larger than _PyTime_t");
441443
t = (_PyTime_t)ts->tv_sec;
442444

443445
int res1 = pytime_mul(&t, SEC_TO_NS);
@@ -466,7 +468,8 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts)
466468
static int
467469
pytime_fromtimeval(_PyTime_t *tp, struct timeval *tv, int raise_exc)
468470
{
469-
Py_BUILD_ASSERT(sizeof(tv->tv_sec) <= sizeof(_PyTime_t));
471+
static_assert(sizeof(tv->tv_sec) <= sizeof(_PyTime_t),
472+
"timeval.tv_sec is larger than _PyTime_t");
470473
_PyTime_t t = (_PyTime_t)tv->tv_sec;
471474

472475
int res1 = pytime_mul(&t, SEC_TO_NS);
@@ -537,7 +540,8 @@ pytime_from_object(_PyTime_t *tp, PyObject *obj, _PyTime_round_t round,
537540
return -1;
538541
}
539542

540-
Py_BUILD_ASSERT(sizeof(long long) <= sizeof(_PyTime_t));
543+
static_assert(sizeof(long long) <= sizeof(_PyTime_t),
544+
"_PyTime_t is smaller than long long");
541545
_PyTime_t ns = (_PyTime_t)sec;
542546
if (pytime_mul(&ns, unit_to_ns) < 0) {
543547
pytime_overflow();
@@ -589,7 +593,8 @@ PyObject *
589593
_PyTime_AsNanosecondsObject(_PyTime_t t)
590594
{
591595
_PyTime_t ns = pytime_as_nanoseconds(t);
592-
Py_BUILD_ASSERT(sizeof(long long) >= sizeof(_PyTime_t));
596+
static_assert(sizeof(long long) >= sizeof(_PyTime_t),
597+
"_PyTime_t is larger than long long");
593598
return PyLong_FromLongLong((long long)ns);
594599
}
595600

@@ -984,15 +989,17 @@ py_mach_timebase_info(_PyTime_t *pnumer, _PyTime_t *pdenom, int raise)
984989
_PyTime_t. In practice, timebase uses uint32_t, so casting cannot
985990
overflow. At the end, only make sure that the type is uint32_t
986991
(_PyTime_t is 64-bit long). */
987-
Py_BUILD_ASSERT(sizeof(timebase.numer) < sizeof(_PyTime_t));
988-
Py_BUILD_ASSERT(sizeof(timebase.denom) < sizeof(_PyTime_t));
992+
static_assert(sizeof(timebase.numer) <= sizeof(_PyTime_t),
993+
"timebase.numer is larger than _PyTime_t");
994+
static_assert(sizeof(timebase.denom) <= sizeof(_PyTime_t),
995+
"timebase.denom is larger than _PyTime_t");
989996

990-
/* Make sure that (ticks * timebase.numer) cannot overflow in
991-
_PyTime_MulDiv(), with ticks < timebase.denom.
997+
/* Make sure that _PyTime_MulDiv(ticks, timebase_numer, timebase_denom)
998+
cannot overflow.
992999
9931000
Known time bases:
9941001
995-
* always (1, 1) on Intel
1002+
* (1, 1) on Intel
9961003
* (1000000000, 33333335) or (1000000000, 25000000) on PowerPC
9971004
9981005
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)
10191026

10201027
#if defined(MS_WINDOWS)
10211028
ULONGLONG ticks = GetTickCount64();
1022-
Py_BUILD_ASSERT(sizeof(ticks) <= sizeof(_PyTime_t));
1023-
_PyTime_t t = (_PyTime_t)ticks;
1029+
static_assert(sizeof(ticks) <= sizeof(_PyTime_t),
1030+
"ULONGLONG is larger than _PyTime_t");
1031+
_PyTime_t t;
1032+
if (ticks <= (ULONGLONG)_PyTime_MAX) {
1033+
t = (_PyTime_t)ticks;
1034+
}
1035+
else {
1036+
// GetTickCount64() maximum is larger than _PyTime_t maximum:
1037+
// ULONGLONG is unsigned, whereas _PyTime_t is signed.
1038+
t = _PyTime_MAX;
1039+
}
10241040

10251041
int res = pytime_mul(&t, MS_TO_NS);
10261042
*tp = t;
@@ -1211,7 +1227,8 @@ py_get_win_perf_counter(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
12111227
/* Make sure that casting LONGLONG to _PyTime_t cannot overflow,
12121228
both types are signed */
12131229
_PyTime_t ticks;
1214-
Py_BUILD_ASSERT(sizeof(ticksll) <= sizeof(ticks));
1230+
static_assert(sizeof(ticksll) <= sizeof(ticks),
1231+
"LONGLONG is larger than _PyTime_t");
12151232
ticks = (_PyTime_t)ticksll;
12161233

12171234
_PyTime_t ns = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)frequency);

0 commit comments

Comments
 (0)