Skip to content

Commit

Permalink
MAINT: Const qualify UFunc inner loops (numpygh-15355)
Browse files Browse the repository at this point in the history
This PR const qualifies the dimension and strides arguments of PyUFuncGenericFunction. Const qualified arguments make it simpler to reason about the behaviour of these ufuncs and prevents accidental mutation. As the const is now required this PR also const qualifies calls to PyUFuncGenericFunction inside the NumPy source code.

This closes numpy#15252
  • Loading branch information
Kai-Striega authored and seberg committed Jan 22, 2020
1 parent 1d05717 commit e94cec8
Show file tree
Hide file tree
Showing 17 changed files with 371 additions and 365 deletions.
7 changes: 7 additions & 0 deletions doc/release/upcoming_changes/15355.c_api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Const qualify UFunc inner loops
-------------------------------
``UFuncGenericFunction`` now expects pointers to const ``dimension`` and
``strides`` as arguments. This means inner loops may no longer modify
either ``dimension`` or ``strides``. This change leads to an
``incompatible-pointer-types`` warning forcing users to either ignore
the compiler warnings or to const qualify their own loop signatures.
62 changes: 32 additions & 30 deletions doc/source/reference/c-api/ufunc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Functions
signature:
.. c:function:: void loopfunc(
char** args, npy_intp* dimensions, npy_intp* steps, void* data)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* data)
*args*
Expand Down Expand Up @@ -108,8 +108,10 @@ Functions
.. code-block:: c
static void
double_add(char **args, npy_intp *dimensions, npy_intp *steps,
void *extra)
double_add(char **args,
npy_intp const *dimensions,
npy_intp const *steps,
void *extra)
{
npy_intp i;
npy_intp is1 = steps[0], is2 = steps[1];
Expand Down Expand Up @@ -311,37 +313,37 @@ functions stored in the functions member of the PyUFuncObject
structure.
.. c:function:: void PyUFunc_f_f_As_d_d( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_d_d( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_f_f( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_g_g( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_F_F_As_D_D( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_F_F( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_D_D( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_G_G( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_e_e( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_e_e_As_f_f( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_e_e_As_d_d( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
Type specific, core 1-d functions for ufuncs where each
calculation is obtained by calling a function taking one input
Expand All @@ -357,37 +359,37 @@ structure.
C-function that takes double and returns double.
.. c:function:: void PyUFunc_ff_f_As_dd_d( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_ff_f( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_dd_d( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_gg_g( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_FF_F_As_DD_D( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_DD_D( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_FF_F( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_GG_G( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_ee_e( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_ee_e_As_ff_f( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_ee_e_As_dd_d( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
Type specific, core 1-d functions for ufuncs where each
calculation is obtained by calling a function taking two input
Expand All @@ -400,10 +402,10 @@ structure.
to use the underlying function that takes a different data type.
.. c:function:: void PyUFunc_O_O( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
.. c:function:: void PyUFunc_OO_O( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
One-input, one-output, and two-input, one-output core 1-d functions
for the :c:data:`NPY_OBJECT` data type. These functions handle reference
Expand All @@ -413,15 +415,15 @@ structure.
PyObject *)`` for :c:func:`PyUFunc_OO_O`.
.. c:function:: void PyUFunc_O_O_method( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
This general purpose 1-d core function assumes that *func* is a string
representing a method of the input object. For each
iteration of the loop, the Python object is extracted from the array
and its *func* method is called returning the result to the output array.
.. c:function:: void PyUFunc_OO_O_method( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
This general purpose 1-d core function assumes that *func* is a
string representing a method of the input object that takes one
Expand All @@ -431,7 +433,7 @@ structure.
of *args*.
.. c:function:: void PyUFunc_On_Om( \
char** args, npy_intp* dimensions, npy_intp* steps, void* func)
char** args, npy_intp const *dimensions, npy_intp const *steps, void* func)
This is the 1-d core function used by the dynamic ufuncs created
by umath.frompyfunc(function, nin, nout). In this case *func* is a
Expand Down
4 changes: 2 additions & 2 deletions numpy/core/include/numpy/ufuncobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ extern "C" {
*/
typedef void (*PyUFuncGenericFunction)
(char **args,
npy_intp *dimensions,
npy_intp *strides,
npy_intp const *dimensions,
npy_intp const *strides,
void *innerloopdata);

/*
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/src/umath/_operand_flag_tests.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ static PyMethodDef TestMethods[] = {


static void
inplace_add(char **args, npy_intp *dimensions, npy_intp *steps, void *data)
inplace_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *data)
{
npy_intp i;
npy_intp n = dimensions[0];
Expand Down
22 changes: 11 additions & 11 deletions numpy/core/src/umath/_rational_tests.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -936,8 +936,8 @@ DEFINE_CAST(npy_bool,rational,rational y = make_rational_int(x);)
DEFINE_CAST(rational,npy_bool,npy_bool y = rational_nonzero(x);)

#define BINARY_UFUNC(name,intype0,intype1,outtype,exp) \
void name(char** args, npy_intp* dimensions, \
npy_intp* steps, void* data) { \
void name(char** args, npy_intp const *dimensions, \
npy_intp const *steps, void* data) { \
npy_intp is0 = steps[0], is1 = steps[1], \
os = steps[2], n = *dimensions; \
char *i0 = args[0], *i1 = args[1], *o = args[2]; \
Expand Down Expand Up @@ -972,8 +972,8 @@ BINARY_UFUNC(gcd_ufunc,npy_int64,npy_int64,npy_int64,gcd(x,y))
BINARY_UFUNC(lcm_ufunc,npy_int64,npy_int64,npy_int64,lcm(x,y))

#define UNARY_UFUNC(name,type,exp) \
void rational_ufunc_##name(char** args, npy_intp* dimensions, \
npy_intp* steps, void* data) { \
void rational_ufunc_##name(char** args, npy_intp const *dimensions, \
npy_intp const *steps, void* data) { \
npy_intp is = steps[0], os = steps[1], n = *dimensions; \
char *i = args[0], *o = args[1]; \
int k; \
Expand All @@ -996,7 +996,7 @@ UNARY_UFUNC(numerator,npy_int64,x.n)
UNARY_UFUNC(denominator,npy_int64,d(x))

static NPY_INLINE void
rational_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps)
rational_matrix_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps)
{
/* pointers to data for input and output arrays */
char *ip1 = args[0];
Expand Down Expand Up @@ -1041,8 +1041,8 @@ rational_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps)


static void
rational_gufunc_matrix_multiply(char **args, npy_intp *dimensions,
npy_intp *steps, void *NPY_UNUSED(func))
rational_gufunc_matrix_multiply(char **args, npy_intp const *dimensions,
npy_intp const *steps, void *NPY_UNUSED(func))
{
/* outer dimensions counter */
npy_intp N_;
Expand All @@ -1066,8 +1066,8 @@ rational_gufunc_matrix_multiply(char **args, npy_intp *dimensions,


static void
rational_ufunc_test_add(char** args, npy_intp* dimensions,
npy_intp* steps, void* data) {
rational_ufunc_test_add(char** args, npy_intp const *dimensions,
npy_intp const *steps, void* data) {
npy_intp is0 = steps[0], is1 = steps[1], os = steps[2], n = *dimensions;
char *i0 = args[0], *i1 = args[1], *o = args[2];
int k;
Expand All @@ -1082,8 +1082,8 @@ rational_ufunc_test_add(char** args, npy_intp* dimensions,


static void
rational_ufunc_test_add_rationals(char** args, npy_intp* dimensions,
npy_intp* steps, void* data) {
rational_ufunc_test_add_rationals(char** args, npy_intp const *dimensions,
npy_intp const *steps, void* data) {
npy_intp is0 = steps[0], is1 = steps[1], os = steps[2], n = *dimensions;
char *i0 = args[0], *i1 = args[1], *o = args[2];
int k;
Expand Down
6 changes: 4 additions & 2 deletions numpy/core/src/umath/_struct_ufunc_tests.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
* docs.python.org .
*/

static void add_uint64_triplet(char **args, npy_intp *dimensions,
npy_intp* steps, void* data)
static void add_uint64_triplet(char **args,
npy_intp const *dimensions,
npy_intp const* steps,
void* data)
{
npy_intp i;
npy_intp is1=steps[0];
Expand Down
12 changes: 6 additions & 6 deletions numpy/core/src/umath/_umath_tests.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ char *inner1d_signature = "(i),(i)->()";
*/

static void
@TYPE@_inner1d(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
@TYPE@_inner1d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
{
INIT_OUTER_LOOP_3
npy_intp di = dimensions[0];
Expand Down Expand Up @@ -106,7 +106,7 @@ char *innerwt_signature = "(i),(i),(i)->()";
*/

static void
@TYPE@_innerwt(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
@TYPE@_innerwt(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
{
INIT_OUTER_LOOP_4
npy_intp di = dimensions[0];
Expand Down Expand Up @@ -143,7 +143,7 @@ char *matmul_signature = "(m?,n),(n,p?)->(m?,p?)";
*/

static void
@TYPE@_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
@TYPE@_matrix_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
{
/* no BLAS is available */
INIT_OUTER_LOOP_3
Expand Down Expand Up @@ -212,7 +212,7 @@ char *cross1d_signature = "(3),(3)->(3)";
* out[n, 2] = in1[n, 0]*in2[n, 1] - in1[n, 1]*in2[n, 0]
*/
static void
@TYPE@_cross1d(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
@TYPE@_cross1d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
{
INIT_OUTER_LOOP_3
npy_intp is1=steps[0], is2=steps[1], os = steps[2];
Expand Down Expand Up @@ -252,7 +252,7 @@ char *euclidean_pdist_signature = "(n,d)->(p)";
*/

static void
@TYPE@_euclidean_pdist(char **args, npy_intp *dimensions, npy_intp *steps,
@TYPE@_euclidean_pdist(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
INIT_OUTER_LOOP_2
Expand Down Expand Up @@ -308,7 +308,7 @@ char *cumsum_signature = "(i)->(i)";
*/

static void
@TYPE@_cumsum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
@TYPE@_cumsum(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
{
INIT_OUTER_LOOP_2
npy_intp di = dimensions[0];
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/src/umath/clip.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
_NPY_@name@_MIN(_NPY_@name@_MAX((x), (min)), (max))

NPY_NO_EXPORT void
@name@_clip(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
@name@_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
{
if (steps[1] == 0 && steps[2] == 0) {
/* min and max are constant throughout the loop, the most common case */
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/src/umath/clip.h.src
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* DATETIME, TIMEDELTA#
*/
NPY_NO_EXPORT void
@name@_clip(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
@name@_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
/**end repeat**/

#endif
4 changes: 2 additions & 2 deletions numpy/core/src/umath/fast_loop_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* These expect to have access to the arguments of a typical ufunc loop,
*
* char **args
* npy_intp *dimensions
* npy_intp *steps
* npy_intp const *dimensions
* npy_intp const *steps
*/
#ifndef _NPY_UMATH_FAST_LOOP_MACROS_H_
#define _NPY_UMATH_FAST_LOOP_MACROS_H_
Expand Down
Loading

0 comments on commit e94cec8

Please sign in to comment.