From 08e44780f027ce2d8306557d73a5c07b4dea66a6 Mon Sep 17 00:00:00 2001 From: da-woods Date: Wed, 4 Jan 2023 10:02:35 +0000 Subject: Add support of const fused type memory views (GH-3118) (GH-5076) Backport of https://github.com/cython/cython/pull/3118 Fixes https://github.com/cython/cython/issues/3783 Co-authored-by: Thomas VINCENT --- Cython/Compiler/PyrexTypes.py | 1 + tests/errors/fused_types.pyx | 34 ++++++++++++----- tests/memoryview/numpy_memoryview_readonly.pyx | 50 +++++++++++++++++++++++- tests/run/fused_types.pyx | 53 ++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 12 deletions(-) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 922c39367..c309bd04b 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1561,6 +1561,7 @@ class PythranExpr(CType): class CConstType(BaseType): is_const = 1 + subtypes = ['const_base_type'] def __init__(self, const_base_type): self.const_base_type = const_base_type diff --git a/tests/errors/fused_types.pyx b/tests/errors/fused_types.pyx index 438e46584..6d9dd6879 100644 --- a/tests/errors/fused_types.pyx +++ b/tests/errors/fused_types.pyx @@ -1,4 +1,5 @@ # mode: error +# ticket: 1772 cimport cython from cython import fused_type @@ -64,17 +65,30 @@ ctypedef fused fused2: func(x, y) +cdef fused mix_const_t: + int + const int + +cdef cdef_func_with_mix_const_type(mix_const_t val): + print(val) + +# Mixing const and non-const type makes fused type ambiguous +cdef_func_with_mix_const_type(1) + + _ERRORS = u""" -10:15: fused_type does not take keyword arguments -15:33: Type specified multiple times -26:0: Invalid use of fused types, type cannot be specialized -26:4: Not enough types specified to specialize the function, int2_t is still fused +11:15: fused_type does not take keyword arguments +16:33: Type specified multiple times 27:0: Invalid use of fused types, type cannot be specialized 27:4: Not enough types specified to specialize the function, int2_t is still fused -28:16: Call with wrong number of arguments (expected 2, got 1) -29:16: Call with wrong number of arguments (expected 2, got 3) -36:6: Invalid base type for memoryview slice: int * -39:0: Fused lambdas not allowed -42:5: Fused types not allowed here -45:9: Fused types not allowed here +28:0: Invalid use of fused types, type cannot be specialized +28:4: Not enough types specified to specialize the function, int2_t is still fused +29:16: Call with wrong number of arguments (expected 2, got 1) +30:16: Call with wrong number of arguments (expected 2, got 3) +37:6: Invalid base type for memoryview slice: int * +40:0: Fused lambdas not allowed +43:5: Fused types not allowed here +46:9: Fused types not allowed here +76:0: Invalid use of fused types, type cannot be specialized +76:29: ambiguous overloaded method """ diff --git a/tests/memoryview/numpy_memoryview_readonly.pyx b/tests/memoryview/numpy_memoryview_readonly.pyx index 20b6c7393..f1b289968 100644 --- a/tests/memoryview/numpy_memoryview_readonly.pyx +++ b/tests/memoryview/numpy_memoryview_readonly.pyx @@ -1,10 +1,14 @@ # mode: run # tag: readonly, const, numpy +# ticket: 1772 import numpy as np +cimport cython -def new_array(): - return np.arange(10).astype('float') +def new_array(dtype='float', writeable=True): + array = np.arange(10, dtype=dtype) + array.setflags(write=writeable) + return array ARRAY = new_array() @@ -124,3 +128,45 @@ def test_copy(): rw[1] = 2 rw2[2] = 2 return rw[0], rw[1], rw[2], rw2[0], rw2[1], rw2[2] + + +cdef getmax_floating(const cython.floating[:] x): + """Function with fused type, should work with both ro and rw memoryviews""" + cdef cython.floating max_val = - float('inf') + for val in x: + if val > max_val: + max_val = val + return max_val + + +def test_mmview_const_fused_cdef(): + """Test cdef function with const fused type memory view as argument. + + >>> test_mmview_const_fused_cdef() + """ + cdef float[:] data_rw = new_array(dtype='float32') + assert getmax_floating(data_rw) == 9 + + cdef const float[:] data_ro = new_array(dtype='float32', writeable=False) + assert getmax_floating(data_ro) == 9 + + +def test_mmview_const_fused_def(const cython.floating[:] x): + """Test def function with const fused type memory view as argument. + + With read-write numpy array: + + >>> test_mmview_const_fused_def(new_array('float32', writeable=True)) + 0.0 + >>> test_mmview_const_fused_def(new_array('float64', writeable=True)) + 0.0 + + With read-only numpy array: + + >>> test_mmview_const_fused_def(new_array('float32', writeable=False)) + 0.0 + >>> test_mmview_const_fused_def(new_array('float64', writeable=False)) + 0.0 + """ + cdef cython.floating result = x[0] + return result diff --git a/tests/run/fused_types.pyx b/tests/run/fused_types.pyx index 7b27b9449..de63fbdd0 100644 --- a/tests/run/fused_types.pyx +++ b/tests/run/fused_types.pyx @@ -1,4 +1,5 @@ # mode: run +# ticket: 1772 cimport cython from cython.view cimport array @@ -363,6 +364,22 @@ def test_fused_memslice_dtype_repeated_2(cython.floating[:] array1, cython.float """ print cython.typeof(array1), cython.typeof(array2), cython.typeof(array3) +def test_fused_const_memslice_dtype_repeated(const cython.floating[:] array1, cython.floating[:] array2): + """Test fused types memory view with one being const + + >>> sorted(test_fused_const_memslice_dtype_repeated.__signatures__) + ['double', 'float'] + + >>> test_fused_const_memslice_dtype_repeated(get_array(8, 'd'), get_array(8, 'd')) + const double[:] double[:] + >>> test_fused_const_memslice_dtype_repeated(get_array(4, 'f'), get_array(4, 'f')) + const float[:] float[:] + >>> test_fused_const_memslice_dtype_repeated(get_array(8, 'd'), get_array(4, 'f')) + Traceback (most recent call last): + ValueError: Buffer dtype mismatch, expected 'double' but got 'float' + """ + print cython.typeof(array1), cython.typeof(array2) + def test_cython_numeric(cython.numeric arg): """ Test to see whether complex numbers have their utility code declared @@ -388,6 +405,18 @@ def test_index_fused_args(cython.floating f, ints_t i): """ _test_index_fused_args[cython.floating, ints_t](f, i) +cdef _test_index_const_fused_args(const cython.floating f, const ints_t i): + print(cython.typeof(f), cython.typeof(i)) + +def test_index_const_fused_args(const cython.floating f, const ints_t i): + """Test indexing function implementation with const fused type args + + >>> import cython + >>> test_index_const_fused_args[cython.double, cython.int](2.0, 3) + ('const double', 'const int') + """ + _test_index_const_fused_args[cython.floating, ints_t](f, i) + def test_composite(fused_composite x): """ @@ -404,6 +433,30 @@ def test_composite(fused_composite x): return 2 * x +cdef cdef_func_const_fused_arg(const cython.floating val, + const fused_type1 * ptr_to_const, + const (cython.floating *) const_ptr): + print(val, cython.typeof(val)) + print(ptr_to_const[0], cython.typeof(ptr_to_const[0])) + print(const_ptr[0], cython.typeof(const_ptr[0])) + + ptr_to_const = NULL # pointer is not const, value is const + const_ptr[0] = 0.0 # pointer is const, value is not const + +def test_cdef_func_with_const_fused_arg(): + """Test cdef function with const fused type argument + + >>> test_cdef_func_with_const_fused_arg() + (0.0, 'const float') + (1, 'const int') + (2.0, 'float') + """ + cdef float arg0 = 0.0 + cdef int arg1 = 1 + cdef float arg2 = 2.0 + cdef_func_const_fused_arg(arg0, &arg1, &arg2) + + ### see GH3642 - presence of cdef inside "unrelated" caused a type to be incorrectly inferred cdef unrelated(cython.floating x): cdef cython.floating t = 1 -- cgit v1.2.1