From c67985dc5d66509cbc02a1cf62064b399cca31e2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 7 Oct 2026 15:55:04 +0200 Subject: [PATCH] gh-158942: Use more unique variable names in Py_MIN/MAX/ABS() If Py_MIN/MAX/ABS() macros are called on an existing "_x" or "_y" variable name, the macro local variables may be overriden by the external variables. So use more unique names in the macros. Add test_min_max_abs_macros() to _testlimitedcapi. --- Include/pymacro.h | 16 ++++++------- Modules/_testlimitedcapi/object.c | 39 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/Include/pymacro.h b/Include/pymacro.h index 122d26315b51b9..41dfd9155b9b24 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -118,20 +118,20 @@ /* Minimum value between x and y */ # define Py_MIN(x, y) \ __extension__ \ - ({ _Py_TYPEOF (x) _x = (x); \ - _Py_TYPEOF (y) _y = (y); \ - _x < _y ? _x : _y; }) + ({ _Py_TYPEOF (x) _PyMIN_x = (x); \ + _Py_TYPEOF (y) _PyMIN_y = (y); \ + _PyMIN_x < _PyMIN_y ? _PyMIN_x : _PyMIN_y; }) /* Maximum value between x and y */ # define Py_MAX(x, y) \ __extension__ \ - ({ _Py_TYPEOF (x) _x = (x); \ - _Py_TYPEOF (y) _y = (y); \ - _x > _y ? _x : _y; }) + ({ _Py_TYPEOF (x) _PyMAX_x = (x); \ + _Py_TYPEOF (y) _PyMAX_y = (y); \ + _PyMAX_x > _PyMAX_y ? _PyMAX_x : _PyMAX_y; }) /* Absolute value of the number x */ # define Py_ABS(x) \ __extension__ \ - ({ _Py_TYPEOF (x) _x = (x); \ - _x < 0 ? -_x : _x; }) + ({ _Py_TYPEOF (x) _PyABS_x = (x); \ + _PyABS_x < 0 ? -_PyABS_x : _PyABS_x; }) #else /* Minimum value between x and y */ # define Py_MIN(x, y) (((x) > (y)) ? (y) : (x)) diff --git a/Modules/_testlimitedcapi/object.c b/Modules/_testlimitedcapi/object.c index 03debb56b33c4b..78cb98dc08fef9 100644 --- a/Modules/_testlimitedcapi/object.c +++ b/Modules/_testlimitedcapi/object.c @@ -241,6 +241,44 @@ test_refcount_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) TEST_REFCOUNT(); } + +// Test Py_MIN(), Py_MAX() and Py_ABS() macros. +// On GCC/clang, they are implemented as a statement expression to only +// evaluate each argument only once. +static PyObject* +test_min_max_abs_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + int x = 5, y = 7, z = -11; + + // Simple usage + assert(Py_MIN(x, y) == 5); + assert(Py_MAX(x, y) == 7); + assert(Py_ABS(z) == 11); + + // Combined macros + assert(Py_MIN(x, Py_MIN(y, z)) == -11); + assert(Py_MAX(x, Py_MAX(y, z)) == 7); + assert(Py_MIN(x, Py_MAX(y, z)) == 5); + assert(Py_MAX(x, Py_MIN(y, z)) == 5); + assert(Py_ABS(Py_ABS(z)) == 11); + +#if ((defined(__GNUC__) || defined(__clang__)) \ + && defined(_Py_TYPEOF) && !defined(__cplusplus)) + // Check that arguments are only evaluated once + int a = 5, b = 7, c = -11; + assert(Py_MIN(++a, ++b) == 6); + assert(a == 6); + assert(b == 8); + assert(Py_MAX(++a, ++b) == 9); + assert(a == 7); + assert(b == 9); + assert(Py_ABS(--c) == 12); + assert(c == -12); +#endif + + Py_RETURN_NONE; +} + #undef Py_NewRef #undef Py_XNewRef @@ -315,6 +353,7 @@ static PyMethodDef test_methods[] = { {"test_py_setref", test_py_setref, METH_NOARGS}, {"test_refcount_macros", test_refcount_macros, METH_NOARGS}, {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS}, + {"test_min_max_abs_macros", test_min_max_abs_macros, METH_NOARGS}, {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {NULL},