Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork34k
Description
Bug report
Bug description:
MacrosPy_BUILD_ASSERT andPy_BUILD_ASSERT_EXPR defined inInclude/pymacro.h are broken whencond is not a constant expression, becausesizeof is allowed to apply on variable-length arrays(VLA) since C99 and with compiler extension since C89.
/* Assert a build-time dependency, as an expression. Your compile will fail if the condition isn't true, or can't be evaluated by the compiler. This can be used in an expression: its value is 0. Example: #define foo_to_char(foo) \ ((char *)(foo) \ + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) Written by Rusty Russell, public domain, http://ccodearchive.net/ */#definePy_BUILD_ASSERT_EXPR(cond) \ (sizeof(char [1 - 2*!(cond)]) - 1)#definePy_BUILD_ASSERT(cond) do { \ (void)Py_BUILD_ASSERT_EXPR(cond); \ } while(0)
Following code compiles on Clang and GCC without error:
voidfoo(intparam){Py_BUILD_ASSERT(param>0);}
Since CPython is now using C11 (PEP7) andstatic_assert is already used in other public internal headers(e.g.Include/internal/pycore_long.h),Py_BUILD_ASSERT should be deprecated and usestatic_assert instead.
Since they are defined in a public header of CPython without a "_Py" prefix, removing them might break third-party code.
Py_BUILD_ASSERT can be defined tostatic_assert in a way with deprecating warning message:
#definePy_BUILD_ASSERT(cond) do { \ Py_DEPRECATED(3.14) \ 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)
Py_BUILD_ASSERT_EXPR is used (and only used) in another macroPy_ARRAY_LENGTH, so it can't be deprecated yet.
FixingPy_BUILD_ASSERT_EXPR is tricky, most because of old non-conformant MSVC1. And we need it also working in C++. The easiest fix is to change documentation so that it should be used only with constant expression or there's no assertion otherwise. But If we want to make it mandatory, here's a workaround:
#if defined(__cplusplus)template<typenameT>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"); };# definePy_BUILD_ASSERT_EXPR(cond) \ (!sizeof(_Py_BUILD_ASSERT_EXPR_prohibit_vla<char[1 - 2 * !(cond)]>))#elif defined(_MSC_VER)# definePy_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# definePy_BUILD_ASSERT_EXPR(cond) \ (!sizeof( \ enum { \ Py_CONCAT(_Py_BUILD_ASSERT_EXPR_prohibit_vla_,__LINE__) = \ sizeof(char[1 - 2 * !(cond)]) \ } \ ))#endif
You can view and try the workarounds using the amazing conformance-viewer in amazing Compiler Explorer: forC and forC++.
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
- GH-118124: Fix Py_BUILD_ASSERT on non-constant expression #118125
- gh-118124: Use static_assert() in Py_BUILD_ASSERT() on C11 #118398
- gh-118124: fix assert related C++ checks on Solaris/Illumos #121974
- [3.13] gh-118124: fix assert related C++ checks on Solaris/Illumos (GH-121974) #122108
- [3.12] gh-118124: fix assert related C++ checks on Solaris/Illumos (GH-121974) #122109
Footnotes
One of the unbelievable bugs is that MSVC prior to v19.21 accepts negative-length(implicitly extended to unsigned value) arrays when used as template argument, e.g.
A<char[-1]>.↩