Skip to content

Commit

Permalink
pythongh-118124: Use static_assert() in Py_BUILD_ASSERT() on C11
Browse files Browse the repository at this point in the history
Use static_assert() in Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
on C11 and newer and C++11 and newer.

Add tests to test_cext and test_cppext.
  • Loading branch information
vstinner committed Apr 30, 2024
1 parent 0f79740 commit ec6d1b4
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 16 deletions.
49 changes: 33 additions & 16 deletions Include/pymacro.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,41 @@
/* Argument must be a char or an int in [-128, 127] or [0, 255]. */
#define Py_CHARMASK(c) ((unsigned char)((c) & 0xff))

/* 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/ */
#define Py_BUILD_ASSERT_EXPR(cond) \
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
# define Py_BUILD_ASSERT_EXPR(cond) \
((void)sizeof(struct { int dummy; _Static_assert(cond, #cond); }), \
0)
#else
/* 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/
*/
# define Py_BUILD_ASSERT_EXPR(cond) \
(sizeof(char [1 - 2*!(cond)]) - 1)
#endif

#define Py_BUILD_ASSERT(cond) do { \
(void)Py_BUILD_ASSERT_EXPR(cond); \
} while(0)
#if ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|| (defined(__cplusplus) && __cplusplus >= 201103L))
// Use static_assert() on C11 and newer
# define Py_BUILD_ASSERT(cond) \
do { \
static_assert((cond), #cond); \
} while (0)
#else
# define Py_BUILD_ASSERT(cond) \
do { \
(void)Py_BUILD_ASSERT_EXPR(cond); \
} while(0)
#endif

/* Get the number of elements in a visible array
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_cext/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ _testcext_exec(PyObject *module)
return -1;
}
#endif

// test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int));
assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0);

return 0;
}

Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_cppext/extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ _testcppext_exec(PyObject *module)
if (!result) return -1;
Py_DECREF(result);

// test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int));
assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0);

return 0;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :c:macro:`Py_BUILD_ASSERT` and :c:macro:`Py_BUILD_ASSERT_EXPR` for
non-constant expressions: use ``static_assert()`` on C11 and newer.
Patch by Victor Stinner.

0 comments on commit ec6d1b4

Please sign in to comment.